# 文件IO/系统调用IO
#[[
在标准IO上贯穿始终的一个类型 FILE。通过看其定义所在的头文件, 发现 FILE 是一个结构体,
里面有非常多的字段。

而在系统调用IO上, 也有一个贯穿始终的内容: 文件描述符(file descriptor, fd)。

之前的标准IO, 我们是捏着一个 FILE* 来进行文件的打开关闭、读写等操作。

fd是在文件IO中贯穿始终的类型。

fd

系统调用IO操作: open close read write lseek。

标准IO的实现是通过系统调用IO的。

IO的效率问题。

文件IO与标准IO的区别。

文件共享。

原子操作。

程序中的重定向: dup, dup2。

同步: sync, fsync, fdatasync。

提到文件描述符, 那么一定不能离开 fcntl(); 函数。

ioctl();在偏硬件开发的时候一定会使用到这个函数。

/dev/fd/目录

]]

#[[
FILE这个结构体中的字段一定包含了能够操作文件所有的属性的内容。

其中, 你凭什么读完这个字节, 就知道是读下一个字节, 或者说写完了当前位置就移动到下一个位置。
所以, FILE这个结构体中一定有一个字段是文件位置指针, 可能名字为 pos字段。




打开一个文件, 一个文件一定有一个标识。
每一个文件有一个唯一的标识: 这个标识是 inode, 一个文件有一个唯一的 inode号。
这个 inode号标识了一个存在磁盘上的文件。

调用open();打开一个文件, 也会产生一个结构体, 这个结构体应该和 FILE 类似,
也有一些关于文件属性的字段, 其中一个字段是关于 文件位置的 pos字段。
这个结构体中存储了 open();这个文件的几乎所有的属性信息。

然后, 文件IO当中其实可以把指向这个结构体的指针返回给用户, 这样就类似于捏着一个 FILE* 的指针,
来操作文件。
但是, 文件IO把数据结构隐藏起来了, 根本不让用户看到定义了一个什么类型的结构体。
它定义了一个数组, 然后将指向一个这样的结构体的指针存储到了数组中的某个位置来。

然后open();返回的是当前存放这个结构体指针的数组的下标, 也就是一个整型数。

所以, 文件描述符是一个整型数, 一个数组下标。

如果在open();另外一个文件, 这个文件也会关联一个结构体, 一个指向这个结构体的指针也
会存储在上述的那个数组中的某个位置, 然后返回这个数组位置的下标。



所以, 因为stdio是依赖sysio的, 所以fopen();函数打开一个文件, 是依赖于
open(); 所以, 在产生一个 FILE 结构体的同时一定产生了一个上述与文件关联的结构体。
所以, FILE 结构体中一定还有一个字段成员是: fd。


这个结构体是由 open();产生, 还有一个函数 close(); 这个close();函数的作用就是
将这个结构体释放掉。

fclose();也是在销毁 FILE 结构体的同时, 也会把open();与这个文件关联的结构体也销毁掉。


这个数组有多大?
其实就是 ulimit -a 出来的限制的打开最多的文件的大小: 1024 个。

如果使用 ulimit 来更改这个打开最多的文件的大小限制, 其实就是在更改这个数组的大小。


文件描述符优先使用当前可用范围内最小的那个。


那么这个数组是存储在哪呢?
这个数组是存在一个进程空间里面的。
int fds[1024];

每一个进程都会有一个这样的数组。

如果两个进程打开同一个文件, 不会都拿到一个与这个文件关联的结构体, 而是两份,
打开同一个文件, 就会关联一个与这个文件的结构体。谁执行 open(); 就会产生一个与这个文件关联的结构体。


同一个文件, 在一个进程中打开了两次, 或者是更多的次数, 每次open();都会产生一个这样的结构体。
这些结构体都是与这个文件inode关联的, 而且在数组中存储这个结构体的指针, 返回数组下标。这样
就拿到了两个文件描述符, 但是是关联的同一个文件(inode)。




还有一种情况是, 我们可能需要这种情况发生:

在数组中将某个数组元素的值拷贝一份到数组的某个下标, 那么就是例如下标4和下标6都指向
了这个与文件关联的结构体。

此时如果close(4); 那么这个结构体会不会销毁?
不会, linux不会出现这种漏洞, 如果把 close(4); 那么不会销毁这个与文件关联的结构体。
如果销毁了, 数组6的位置存放的就是悬空指针。

一个文件一定有一个打开计数, 其实就是与这个文件(inode)关联的结构体一定有一个字段是
计数器, 即引用计数。这个引用计数反映了当前的这个结构体被几个指针所引用/所指向。
如果close(4);就会在这个结构体中的引用计数--。--后发现不是0, 那么这块空间就不会释放。

然后再close(6); 此时这个结构体的引用计数变为0了, 这个结构体才会销毁掉释放内存。

]]

#[[
OPEN(2)                                                                         Linux Programmer's Manual                                                                         OPEN(2)

NAME
       open, openat, creat - open and possibly create a file
       打开或者创建一个文件。

SYNOPSIS
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>

       // pathname: 打开的文件, 可以是相对路径, 也可以是绝对路径。
       // flags: 相当于 fopen(); 中的
       // FILE *fopen(const char *pathname, const char *mode);
       // 第二个参数 mode。打开的文件的方式。
       // flags是一个位图(bitmap, 0或者1)。有可选项, 也有必选项。


       int open(const char *pathname, int flags);
       int open(const char *pathname, int flags, mode_t mode);

       int creat(const char *pathname, mode_t mode);

       int openat(int dirfd, const char *pathname, int flags);
       int openat(int dirfd, const char *pathname, int flags, mode_t mode);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       openat():
           Since glibc 2.10:
               _POSIX_C_SOURCE >= 200809L
           Before glibc 2.10:
               _ATFILE_SOURCE

DESCRIPTION
       The open() system call opens the file specified by pathname.  If the specified file does not exist, it may optionally (if O_CREAT is specified in flags) be created by open().
       open() 系统调用打开由路径名指定的文件。如果指定的文件不存在，则可以选择（如果在标志中指定 O_CREAT）由 open() 创建。

       The  return  value  of open() is a file descriptor, a small, nonnegative integer that is used in subsequent system calls (read(2), write(2), lseek(2), fcntl(2), etc.) to refer to
       the open file.  The file descriptor returned by a successful call will be the lowest-numbered file descriptor not currently open for the process.
       open() 的返回值是一个文件描述符，一个小的非负整数，
       在随后的系统调用（read(2)、write(2)、lseek(2)、fcntl(2) 等）中用于引用到打开的文件。
       成功调用返回的文件描述符将是当前未为进程打开的编号最低的文件描述符。

       By default, the new file descriptor is set to remain open across an execve(2) (i.e., the FD_CLOEXEC file descriptor  flag  described  in  fcntl(2)  is  initially  disabled);  the
       O_CLOEXEC flag, described below, can be used to change this default.  The file offset is set to the beginning of the file (see lseek(2)).
       默认情况下，新的文件描述符设置为在 execve(2) 中保持打开状态（即 fcntl(2) 中描述的 FD_CLOEXEC 文件描述符标志最初是禁用的）；
       O_CLOEXEC 标志（如下所述）可用于更改此默认值。文件偏移设置为文件的开头（请参阅 lseek(2)）。

       A  call  to  open()  creates  a new open file description, an entry in the system-wide table of open files.  The open file description records the file offset and the file status
       flags (see below).  A file descriptor is a reference to an open file description; this reference is unaffected if pathname is subsequently removed or modified to refer to a  dif‐
       ferent file.  For further details on open file descriptions, see NOTES.
       调用 open() 会创建一个新的打开文件描述，这是系统范围的打开文件表中的一个条目。
       打开的文件描述记录了文件偏移量和文件状态标志（见下文）。
       文件描述符是对打开文件描述的引用；如果路径名随后被删除或修改以引用不同的文件，则此引用不受影响。有关打开的文件描述的更多详细信息，请参阅 NOTES。

       The  argument  flags  must include one of the following access modes: O_RDONLY, O_WRONLY, or O_RDWR.  These request opening the file read-only, write-only, or read/write, respec‐
       tively.
       参数标志必须包括以下访问模式之一：O_RDONLY、O_WRONLY 或 O_RDWR。这些请求分别以只读、只写或读写方式打开文件。

       参数 flags 必须包括以下访问模式中的一个: O_RDONLY O_WRONLY O_RDWR 。
       以只读、只写、读写方式(三选一)打开一个文件。


       In addition, zero or more file creation flags and file status flags can be bitwise-or'd in flags.  The file creation flags are O_CLOEXEC, O_CREAT, O_DIRECTORY, O_EXCL,  O_NOCTTY,
       O_NOFOLLOW,  O_TMPFILE,  and O_TRUNC.  The file status flags are all of the remaining flags listed below.  The distinction between these two groups of flags is that the file cre‐
       ation flags affect the semantics of the open operation itself, while the file status flags affect the semantics of subsequent I/O operations.  The file status flags  can  be  re‐
       trieved and (in some cases) modified; see fcntl(2) for details.
       此外，零个或多个文件创建标志和文件状态标志可以按位或在标志中进行。
       文件创建标志是 O_CLOEXEC、O_CREAT、O_DIRECTORY、O_EXCL、O_NOCTTY、O_NOFOLLOW、O_TMPFILE 和 O_TRUNC。
       文件状态标志是下面列出的所有剩余标志。
       这两组标志的区别在于文件创建标志影响打开操作本身的语义，而文件状态标志影响后续 IO 操作的语义。
       可以检索和（在某些情况下）修改文件状态标志；有关详细信息，请参阅 fcntl(2)。

       另外, 0个或多个文件的创建flags和文件状态flags可以以按位或放在参数 flags中。

       The full list of file creation flags and file status flags is as follows:
       文件创建标志和文件状态标志的完整列表如下:

       O_APPEND

       以追加的方式打开一个文件, 用的比较多。

              The  file  is opened in append mode.  Before each write(2), the file offset is positioned at the end of the file, as if with lseek(2).  The modification of the file offset
              and the write operation are performed as a single atomic step.
              文件以追加模式打开。在每次 write(2) 之前，文件偏移量位于文件末尾，就像使用 lseek(2) 一样。
              文件偏移的修改和写入操作作为单个原子步骤执行。

              O_APPEND may lead to corrupted files on NFS filesystems if more than one process appends data to a file at once.  This is because NFS does not support appending to a file,
              so the client kernel has to simulate it, which can't be done without a race condition.
              如果多个进程同时将数据附加到一个文件，O_APPEND 可能会导致 NFS 文件系统上的文件损坏。
              这是因为 NFS 不支持附加到文件，所以客户端内核必须模拟它，这在没有竞争条件的情况下是无法完成的。

       O_ASYNC
       信号驱动IO。
              Enable  signal-driven  I/O: generate a signal (SIGIO by default, but this can be changed via fcntl(2)) when input or output becomes possible on this file descriptor.  This
              feature is available only for terminals, pseudoterminals, sockets, and (since Linux 2.6) pipes and FIFOs.  See fcntl(2) for further details.  See also BUGS, below.
              启用信号驱动 IO：在此文件描述符上可以输入或输出时生成信号（默认情况下为 SIGIO，但可以通过 fcntl(2) 更改）。
              此功能仅适用于终端、伪终端、套接字和（自 Linux 2.6 起）管道和 FIFO。有关更多详细信息，请参阅 fcntl(2)。另请参阅下面的 BUGS。

       O_CLOEXEC (since Linux 2.6.23)
              Enable the close-on-exec flag for the new file descriptor.  Specifying this flag permits a program to avoid additional fcntl(2) F_SETFD operations to  set  the  FD_CLOEXEC
              flag.
              为新文件描述符启用 close-on-exec 标志。指定此标志允许程序避免额外的 fcntl(2) F_SETFD 操作来设置 FD_CLOEXEC 标志。

              Note that the use of this flag is essential in some multithreaded programs, because using a separate fcntl(2) F_SETFD operation to set the FD_CLOEXEC flag does not suffice
              to avoid race conditions where one thread opens a file descriptor and attempts to set its close-on-exec flag using fcntl(2) at the same  time  as  another  thread  does  a
              fork(2)  plus execve(2).  Depending on the order of execution, the race may lead to the file descriptor returned by open() being unintentionally leaked to the program exe‐
              cuted by the child process created by fork(2).  (This kind of race is in principle possible for any system call that creates a file  descriptor  whose  close-on-exec  flag
              should be set, and various other Linux system calls provide an equivalent of the O_CLOEXEC flag to deal with this problem.)
              请注意，在某些多线程程序中使用此标志是必不可少的，
              因为使用单独的 fcntl(2) F_SETFD 操作来设置 FD_CLOEXEC 标志不足以避免一个线程打开文件描述符并尝试设置其关闭的竞争条件
               - 在另一个线程执行 fork(2) 加 execve(2) 的同时使用 fcntl(2) 的 on-exec 标志。
               根据执行顺序，竞争可能会导致 open() 返回的文件描述符无意中泄露给由 fork(2) 创建的子进程执行的程序。
                （这种竞争原则上对于任何创建文件描述符的系统调用都是可能的，该文件描述符应该设置 close-on-exec 标志，
                并且各种其他 Linux 系统调用提供了等效的 O_CLOEXEC 标志来处理这个问题。）

       O_CREAT

       创建一个新文件。

              If pathname does not exist, create it as a regular file.
              如果路径名不存在，则将其创建为常规文件。

              The owner (user ID) of the new file is set to the effective user ID of the process.
              新文件的所有者（用户 ID）设置为进程的有效用户 ID。

              The  group ownership (group ID) of the new file is set either to the effective group ID of the process (System V semantics) or to the group ID of the parent directory (BSD
              semantics).  On Linux, the behavior depends on whether the set-group-ID mode bit is set on the parent directory: if that bit is set, then BSD semantics  apply;  otherwise,
              System V semantics apply.  For some filesystems, the behavior also depends on the bsdgroups and sysvgroups mount options described in mount(8)).
              新文件的组所有权（组 ID）被设置为进程的有效组 ID（System V 语义）或父目录的组 ID（BSD 语义）。
              在 Linux 上，行为取决于是否在父目录上设置了 set-group-ID 模式位：如果设置了该位，则应用 BSD 语义；否则，系统 V 语义适用。
              对于某些文件系统，行为还取决于 mount(8) 中描述的 bsdgroups 和 sysvgroups 挂载选项。

              The  mode  argument specifies the file mode bits be applied when a new file is created.  This argument must be supplied when O_CREAT or O_TMPFILE is specified in flags; if
              neither O_CREAT nor O_TMPFILE is specified, then mode is ignored.  The effective mode is modified by the process's umask in the usual way: in the absence of a default ACL,
              the  mode  of the created file is (mode & ~umask).  Note that this mode applies only to future accesses of the newly created file; the open() call that creates a read-only
              file may well return a read/write file descriptor.
              mode 参数指定创建新文件时应用的文件模式位。
              在标志中指定 O_CREAT 或 O_TMPFILE 时必须提供此参数；如果 O_CREAT 和 O_TMPFILE 均未指定，则忽略模式。
              有效模式由进程的 umask 以通常的方式修改：在没有默认 ACL 的情况下，创建的文件的模式是 (mode & ~umask)。
              请注意，此模式仅适用于新创建的文件的未来访问；创建只读文件的 open() 调用很可能返回一个读写文件描述符。

              The following symbolic constants are provided for mode:
              为模式提供了以下符号常量:

              S_IRWXU  00700 user (file owner) has read, write, and execute permission
              S_IRWXU 00700 用户（文件所有者）具有读、写和执行权限

              S_IRUSR  00400 user has read permission
              S_IRUSR 00400 用户有读权限

              S_IWUSR  00200 user has write permission
              S_IWUSR 00200 用户有写权限

              S_IXUSR  00100 user has execute permission
              S_IXUSR 00100 用户有执行权限

              S_IRWXG  00070 group has read, write, and execute permission
              S_IRWXG 00070 组有读、写、执行权限

              S_IRGRP  00040 group has read permission
              S_IRGRP 00040 组有读权限

              S_IWGRP  00020 group has write permission
              S_IWGRP 00020 组有写权限

              S_IXGRP  00010 group has execute permission
              S_IXGRP 00010 组有执行权限

              S_IRWXO  00007 others have read, write, and execute permission
              S_IRWXO 00007 其他人有读、写和执行权限

              S_IROTH  00004 others have read permission
              S_IROTH 00004 其他人有读取权限

              S_IWOTH  00002 others have write permission
              S_IWOTH 00002 其他人有写权限

              S_IXOTH  00001 others have execute permission
              S_IXOTH 00001 其他人有执行权限

              According to POSIX, the effect when other bits are set in mode is unspecified.  On Linux, the following bits are also honored in mode:
              根据 POSIX，在模式中设置其他位时的效果未指定。在 Linux 上，以下位也适用于模式：

              S_ISUID  0004000 set-user-ID bit
              S_ISUID 0004000 设置用户 ID 位

              S_ISGID  0002000 set-group-ID bit (see inode(7)).
              S_ISGID 0002000 设置组 ID 位（请参阅 inode(7)）。

              S_ISVTX  0001000 sticky bit (see inode(7)).
              S_ISVTX 0001000 粘滞位（请参阅 inode(7)）。

       O_DIRECT (since Linux 2.4.10)
       O_DIRECT（自 Linux 2.4.10 起）
              Try to minimize cache effects of the I/O to and from this file.  In general this will degrade performance, but it is useful in special situations, such  as  when  applica‐
              tions  do  their own caching.  File I/O is done directly to/from user-space buffers.  The O_DIRECT flag on its own makes an effort to transfer data synchronously, but does
              not give the guarantees of the O_SYNC flag that data and necessary metadata are transferred.  To guarantee synchronous I/O, O_SYNC must be used in  addition  to  O_DIRECT.
              See NOTES below for further discussion.
              尝试最小化 IO 对该文件的缓存影响。一般来说，这会降低性能，但它在特殊情况下很有用，例如当应用程序进行自己的缓存时。文件 IO 直接从用户空间缓冲区完成。 O_DIRECT 标志本身努力同步传输数据，但不保证 O_SYNC 标志传输数据和必要的元数据。为了保证同步IO，除了O_DIRECT之外，还必须使用O_SYNC。有关进一步讨论，请参阅下面的注释。

              A semantically similar (but deprecated) interface for block devices is described in raw(8).
              raw(8) 中描述了一个语义相似（但已弃用）的块设备接口。

       O_DIRECTORY
              If  pathname  is  not a directory, cause the open to fail.  This flag was added in kernel version 2.1.126, to avoid denial-of-service problems if opendir(3) is called on a
              FIFO or tape device.
              如果路径名不是目录，则导致打开失败。此标志是在内核版本 2.1.126 中添加的，以避免在 FIFO 或磁带设备上调用 opendir(3) 时出现拒绝服务问题。

       O_DSYNC
              Write operations on the file will complete according to the requirements of synchronized I/O data integrity completion.
              对文件的写操作会按照同步IO数据完整性完成的要求完成。

              By the time write(2) (and similar) return, the output data has been transferred to the underlying hardware, along with any file metadata that would be required to retrieve
              that data (i.e., as though each write(2) was followed by a call to fdatasync(2)).  See NOTES below.
              到 write(2)（和类似的）返回时，输出数据已被传输到底层硬件，以及检索该数据所需的任何文件元数据（即，好像每个 write(2) 后跟调用 fdatasync(2))。请参阅下面的注释。

       O_EXCL Ensure that this call creates the file: if this flag is specified in conjunction with O_CREAT, and pathname already exists, then open() fails with the error EEXIST.
       O_EXCL 确保此调用创建文件：如果此标志与 O_CREAT 一起指定，并且路径名已存在，则 open() 失败并显示错误 EEXIST。

       强调是创建的一个新文件, 和 O_CREAT 配合使用。

              When these two flags are specified, symbolic links are not followed: if pathname is a symbolic link, then open() fails regardless of where the symbolic link points.
              当指定了这两个标志时，不遵循符号链接：如果路径名是符号链接，则无论符号链接指向何处，open() 都会失败。

              In general, the behavior of O_EXCL is undefined if it is used without O_CREAT.  There is one exception: on Linux 2.6 and later, O_EXCL can be used without O_CREAT if path‐
              name refers to a block device.  If the block device is in use by the system (e.g., mounted), open() fails with the error EBUSY.
              一般来说，如果在没有 O_CREAT 的情况下使用 O_EXCL 的行为是未定义的。有一个例外：在 Linux 2.6 及更高版本上，如果路径名指的是块设备，则可以在没有 O_CREAT 的情况下使用 O_EXCL。如果系统正在使用块设备（例如，已安装），则 open() 失败并显示错误 EBUSY。

              On NFS, O_EXCL is supported only when using NFSv3 or later on kernel 2.6 or later.  In NFS environments where O_EXCL support is not provided, programs that rely on it  for
              performing  locking  tasks  will  contain a race condition.  Portable programs that want to perform atomic file locking using a lockfile, and need to avoid reliance on NFS
              support for O_EXCL, can create a unique file on the same filesystem (e.g., incorporating hostname and PID), and use link(2) to make a link to the lockfile.  If link(2) re‐
              turns 0, the lock is successful.  Otherwise, use stat(2) on the unique file to check if its link count has increased to 2, in which case the lock is also successful.
              在 NFS 上，仅当在内核 2.6 或更高版本上使用 NFSv3 或更高版本时才支持 O_EXCL。在不提供 O_EXCL 支持的 NFS 环境中，依赖它执行锁定任务的程序将包含竞争条件。想要使用锁文件执行原子文件锁定，并且需要避免依赖 NFS 支持 O_EXCL 的便携式程序，可以在同一文件系统上创建唯一文件（例如，合并主机名和 PID），并使用 link(2) 使锁定文件的链接。如果 link(2) 返回 0，则锁定成功。否则，在唯一文件上使用 stat(2) 来检查它的链接数是否增加到 2，在这种情况下锁定也成功。

       O_LARGEFILE
       如果打开的文件比较大的话, 那么最好指定这个。
       但是现在貌似不用了。
              (LFS) Allow files whose sizes cannot be represented in an off_t (but can be represented in an off64_t) to be opened.  The _LARGEFILE64_SOURCE macro must be defined (before
              including any header files) in order to obtain this definition.  Setting the _FILE_OFFSET_BITS feature test macro to 64 (rather than using O_LARGEFILE)  is  the  preferred
              method of accessing large files on 32-bit systems (see feature_test_macros(7)).
              (LFS) 允许打开大小不能用 off_t 表示（但可以用 off64_t 表示）的文件。 _LARGEFILE64_SOURCE 宏必须被定义（在包含任何头文件之前）以获得这个定义。将 _FILE_OFFSET_BITS 功能测试宏设置为 64（而不是使用 O_LARGEFILE）是在 32 位系统上访问大文件的首选方法（请参阅 feature_test_macros(7)）。

       O_NOATIME (since Linux 2.6.8)

       atime: 读取时间/访问时间, 指的是文件最后读的时间。
       不要更新文件的访问时间。
       一般做坏事的时候使用。

              Do not update the file last access time (st_atime in the inode) when the file is read(2).
              读取文件时不要更新文件上次访问时间（inode 中的 st_atime）(2)。

              This flag can be employed only if one of the following conditions is true:
              仅当以下条件之一为真时才能使用此标志:

              *  The effective UID of the process matches the owner UID of the file.
              * 进程的有效 UID 与文件的所有者 UID 匹配。

              *  The calling process has the CAP_FOWNER capability in its user namespace and the owner UID of the file has a mapping in the namespace.
              * 调用进程在其用户命名空间中具有 CAP_FOWNER 功能，并且文件的所有者 UID 在命名空间中具有映射。

              This  flag  is  intended for use by indexing or backup programs, where its use can significantly reduce the amount of disk activity.  This flag may not be effective on all
              filesystems.  One example is NFS, where the server maintains the access time.
              此标志旨在供索引或备份程序使用，使用它可以显着减少磁盘活动量。此标志可能并非对所有文件系统都有效。一个例子是 NFS，服务器维护访问时间。

       O_NOCTTY
              If pathname refers to a terminal device—see tty(4)—it will not become the process's controlling terminal even if the process does not have one.
              如果路径名指的是一个终端设备——参见 tty(4)——它不会成为进程的控制终端，即使进程没有终端设备。

       O_NOFOLLOW

       这是 FreeBSD 上的一个打开策略。

              If pathname is a symbolic link, then the open fails, with the error ELOOP.  Symbolic links in earlier components of the pathname will still be followed.   (Note  that  the
              ELOOP  error  that can occur in this case is indistinguishable from the case where an open fails because there are too many symbolic links found while resolving components
              in the prefix part of the pathname.)
              如果路径名是符号链接，则打开失败，并显示错误 ELOOP。路径名的早期组件中的符号链接仍将被遵循。 （请注意，在这种情况下可能发生的 ELOOP 错误与打开失败的情况无法区分，因为在解析路径名前缀部分中的组件时发现了太多符号链接。）

              This flag is a FreeBSD extension, which was added to Linux in version 2.1.126, and has subsequently been standardized in POSIX.1-2008.
              这个标志是一个 FreeBSD 扩展，它在 2.1.126 版中被添加到 Linux 中，随后在 POSIX.1-2008 中被标准化。

              See also O_PATH below.
              另请参见下面的 O_PATH。

       O_NONBLOCK or O_NDELAY

       阻塞IO和非阻塞IO的区别在哪呢?
       用的函数是同一套, 取决于你在打开这个文件的时候, 是否以阻塞或者非阻塞的方式打开的。
       如果没有加 O_NONBLOCK 那就是阻塞的打开一个文件, 加了就是以非阻塞打开一个文件。

              When possible, the file is opened in nonblocking mode.  Neither the open() nor any subsequent I/O operations on the file descriptor which is returned will cause the  call‐
              ing process to wait.
              如果可能，文件以非阻塞模式打开。返回的文件描述符上的 open() 和任何后续 IO 操作都不会导致调用进程等待。

              Note  that  the  setting  of  this  flag has no effect on the operation of poll(2), select(2), epoll(7), and similar, since those interfaces merely inform the caller about
              whether a file descriptor is "ready", meaning that an I/O operation performed on the file descriptor with the O_NONBLOCK flag clear would not block.
              请注意，此标志的设置对 poll(2)、select(2)、epoll(7) 等的操作没有影响，
              因为这些接口只是通知调用者文件描述符是否“就绪”，
              意思是在 O_NONBLOCK 标志清除的文件描述符上执行的 IO 操作不会阻塞。

              Note that this flag has no effect for regular files and block devices; that is, I/O operations will (briefly) block when device activity is required, regardless of whether
              O_NONBLOCK is set.  Since O_NONBLOCK semantics might eventually be implemented, applications should not depend upon blocking behavior when specifying this flag for regular
              files and block devices.
              请注意，此标志对常规文件和块设备无效；也就是说，无论是否设置了 O_NONBLOCK，IO 操作都会（短暂地）在需要设备活动时阻塞。
              由于 O_NONBLOCK 语义可能最终会被实现，因此在为常规文件和块设备指定此标志时，应用程序不应依赖于阻塞行为。

              For the handling of FIFOs (named pipes), see also fifo(7).  For a discussion of the effect of O_NONBLOCK in conjunction with mandatory file locks and with file leases, see
              fcntl(2).
              有关 FIFO（命名管道）的处理，另请参见 fifo(7)。有关 O_NONBLOCK 与强制文件锁和文件租用结合的效果的讨论，请参阅 fcntl(2)。

       O_PATH (since Linux 2.6.39)
              Obtain  a file descriptor that can be used for two purposes: to indicate a location in the filesystem tree and to perform operations that act purely at the file descriptor
              level.  The file itself is not opened, and other file operations (e.g., read(2), write(2), fchmod(2), fchown(2), fgetxattr(2),  ioctl(2),  mmap(2))  fail  with  the  error
              EBADF.
              获取可用于两个目的的文件描述符：指示文件系统树中的位置和执行纯粹在文件描述符级别执行的操作。文件本身未打开，其他文件操作（例如，read(2)、write(2)、fchmod(2)、fchown(2)、fgetxattr(2)、ioctl(2)、mmap(2)）失败与错误 EBADF。

              The following operations can be performed on the resulting file descriptor:
              可以对生成的文件描述符执行以下操作:

              *  close(2).

              *  fchdir(2), if the file descriptor refers to a directory (since Linux 3.5).

              *  fstat(2) (since Linux 3.6).

              *  fstatfs(2) (since Linux 3.12).

              *  Duplicating the file descriptor (dup(2), fcntl(2) F_DUPFD, etc.).

              *  Getting and setting file descriptor flags (fcntl(2) F_GETFD and F_SETFD).

              *  Retrieving open file status flags using the fcntl(2) F_GETFL operation: the returned flags will include the bit O_PATH.

              *  Passing  the  file  descriptor  as  the  dirfd argument of openat() and the other "*at()" system calls.  This includes linkat(2) with AT_EMPTY_PATH (or via procfs using
                 AT_SYMLINK_FOLLOW) even if the file is not a directory.

              *  Passing the file descriptor to another process via a UNIX domain socket (see SCM_RIGHTS in unix(7)).

              When O_PATH is specified in flags, flag bits other than O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW are ignored.

              Opening a file or directory with the O_PATH flag requires no permissions on the object itself (but does require execute permission on the directories in the path  prefix).
              Depending  on the subsequent operation, a check for suitable file permissions may be performed (e.g., fchdir(2) requires execute permission on the directory referred to by
              its file descriptor argument).  By contrast, obtaining a reference to a filesystem object by opening it with the O_RDONLY flag requires that the caller have  read  permis‐
              sion on the object, even when the subsequent operation (e.g., fchdir(2), fstat(2)) does not require read permission on the object.

              If pathname is a symbolic link and the O_NOFOLLOW flag is also specified, then the call returns a file descriptor referring to the symbolic link.  This file descriptor can
              be used as the dirfd argument in calls to fchownat(2), fstatat(2), linkat(2), and readlinkat(2) with an empty pathname to have the calls operate on the symbolic link.

              If pathname refers to an automount point that has not yet been triggered, so no other filesystem is mounted on it, then the call returns a file descriptor referring to the
              automount  directory  without  triggering  a  mount.   fstatfs(2)  can  then  be used to determine if it is, in fact, an untriggered automount point (.f_type == AUTOFS_SU‐
              PER_MAGIC).

              One use of O_PATH for regular files is to provide the equivalent of POSIX.1's O_EXEC functionality.  This permits us to open a file for which we  have  execute  permission
              but not read permission, and then execute that file, with steps something like the following:

                  char buf[PATH_MAX];
                  fd = open("some_prog", O_PATH);
                  snprintf(buf, PATH_MAX, "/proc/self/fd/%d", fd);
                  execl(buf, "some_prog", (char *) NULL);

              An O_PATH file descriptor can also be passed as the argument of fexecve(3).

       O_SYNC Write  operations  on the file will complete according to the requirements of synchronized I/O file integrity completion (by contrast with the synchronized I/O data integ‐
              rity completion provided by O_DSYNC.)
              O_SYNC 对文件的写操作会按照同步 IO 文件完整性完成的要求完成（与 O_DSYNC 提供的同步 IO 数据完整性完成相对）。

              By the time write(2) (or similar) returns, the output data and associated file metadata have been transferred to the underlying hardware (i.e., as though each write(2) was
              followed by a call to fsync(2)).  See NOTES below.
              到 write(2)（或类似的）返回时，输出数据和关联的文件元数据已传输到底层硬件（即，好像每个 write(2) 之后都调用了 fsync(2)）。请参阅下面的注释。

       O_TMPFILE (since Linux 3.11)
              Create  an unnamed temporary regular file.  The pathname argument specifies a directory; an unnamed inode will be created in that directory's filesystem.  Anything written
              to the resulting file will be lost when the last file descriptor is closed, unless the file is given a name.
              创建一个未命名的临时常规文件。 pathname 参数指定一个目录；将在该目录的文件系统中创建一个未命名的 inode。当最后一个文件描述符关闭时，写入结果文件的任何内容都将丢失，除非为该文件指定了名称。

              O_TMPFILE must be specified with one of O_RDWR or O_WRONLY and, optionally, O_EXCL.  If O_EXCL is not specified, then linkat(2) can be used to link the temporary file into
              the filesystem, making it permanent, using code like the following:
              O_TMPFILE 必须指定为 O_RDWR 或 O_WRONLY 之一，以及可选的 O_EXCL。如果未指定 O_EXCL，则可以使用 linkat(2) 将临时文件链接到文件系统，使其永久化，使用如下代码:

                  char path[PATH_MAX];
                  fd = open("/path/to/dir", O_TMPFILE | O_RDWR,
                                          S_IRUSR | S_IWUSR);

                  /* File I/O on 'fd'... */

                  linkat(fd, NULL, AT_FDCWD, "/path/for/file", AT_EMPTY_PATH);

                  /* If the caller doesn't have the CAP_DAC_READ_SEARCH
                     capability (needed to use AT_EMPTY_PATH with linkat(2)),
                     and there is a proc(5) filesystem mounted, then the
                     linkat(2) call above can be replaced with:

                  snprintf(path, PATH_MAX,  "/proc/self/fd/%d", fd);
                  linkat(AT_FDCWD, path, AT_FDCWD, "/path/for/file",
                                          AT_SYMLINK_FOLLOW);
                  */

              In this case, the open() mode argument determines the file permission mode, as with O_CREAT.
              在这种情况下，open() 模式参数决定了文件权限模式，就像 O_CREAT 一样。

              Specifying  O_EXCL  in conjunction with O_TMPFILE prevents a temporary file from being linked into the filesystem in the above manner.  (Note that the meaning of O_EXCL in
              this case is different from the meaning of O_EXCL otherwise.)
              将 O_EXCL 与 O_TMPFILE 一起指定可防止临时文件以上述方式链接到文件系统中。 （请注意，这种情况下 O_EXCL 的含义与其他情况下 O_EXCL 的含义不同。）

              There are two main use cases for O_TMPFILE:
              O_TMPFILE 有两个主要用例:

              *  Improved tmpfile(3) functionality: race-free creation of temporary files that (1) are automatically deleted when closed; (2) can never be reached via any pathname;  (3)
                 are not subject to symlink attacks; and (4) do not require the caller to devise unique names.
              * 改进的 tmpfile(3) 功能: 竞争地创建临时文件，(1) 在关闭时自动删除； (2) 永远无法通过任何路径名到达； (3) 不受符号链接攻击； (4) 不需要调用者设计唯一的名称。

              *  Creating  a  file  that  is  initially invisible, which is then populated with data and adjusted to have appropriate filesystem attributes (fchown(2), fchmod(2), fsetx‐
                 attr(2), etc.)  before being atomically linked into the filesystem in a fully formed state (using linkat(2) as described above).
              * 创建一个最初不可见的文件，然后填充数据并调整为具有适当的文件系统属性（fchown(2)、fchmod(2)、fsetxattr(2) 等），然后以原子方式链接到文件系统完全形成的状态（使用如上所述的 linkat(2)）。

              O_TMPFILE requires support by the underlying filesystem; only a subset of Linux filesystems provide that support.  In the initial implementation, support was  provided  in
              the  ext2, ext3, ext4, UDF, Minix, and shmem filesystems.  Support for other filesystems has subsequently been added as follows: XFS (Linux 3.15); Btrfs (Linux 3.16); F2FS
              (Linux 3.16); and ubifs (Linux 4.9)
              O_TMPFILE 需要底层文件系统的支持；只有一部分 Linux 文件系统提供这种支持。在最初的实现中，ext2、ext3、ext4、UDF、Minix 和 shmem 文件系统提供了支持。
              随后添加了对其他文件系统的支持，如下所示： XFS (Linux 3.15)； Btrfs（Linux 3.16）； F2FS（Linux 3.16）；和 ubifs (Linux 4.9)

       O_TRUNC
              If the file already exists and is a regular file and the access mode allows writing (i.e., is O_RDWR or O_WRONLY) it will be truncated to length 0.  If the file is a  FIFO
              or terminal device file, the O_TRUNC flag is ignored.  Otherwise, the effect of O_TRUNC is unspecified.
              如果文件已经存在并且是一个普通文件并且访问模式允许写入（即，是 O_RDWR 或 O_WRONLY），它将被截断为长度 0。如果文件是 FIFO 或终端设备文件，则忽略 O_TRUNC 标志。否则，未指定 O_TRUNC 的效果。

   creat()
       A call to creat() is equivalent to calling open() with flags equal to O_CREAT|O_WRONLY|O_TRUNC.
       调用 creat() 等效于调用 open() 的标志等于 O_CREAT|O_WRONLY|O_TRUNC。

   openat()
       The openat() system call operates in exactly the same way as open(), except for the differences described here.
       openat() 系统调用的操作方式与 open() 完全相同，除了此处描述的不同之处。

       If  the  pathname  given  in  pathname is relative, then it is interpreted relative to the directory referred to by the file descriptor dirfd (rather than relative to the current
       working directory of the calling process, as is done by open() for a relative pathname).
       如果 pathname 中给出的路径名是相对的，则它相对于文件描述符 dirfd 引用的目录进行解释（而不是相对于调用进程的当前工作目录，就像 open() 对相对路径名所做的那样） .

       If pathname is relative and dirfd is the special value AT_FDCWD, then pathname is interpreted relative to the current working directory of the calling process (like open()).
       如果路径名是相对的并且 dirfd 是特殊值 AT_FDCWD，则路径名将相对于调用进程的当前工作目录（如 open()）进行解释。

       If pathname is absolute, then dirfd is ignored.
       如果路径名是绝对路径，则忽略 dirfd。

RETURN VALUE
       open(), openat(), and creat() return the new file descriptor, or -1 if an error occurred (in which case, errno is set appropriately).
       open()、openat() 和 creat() 返回新的文件描述符，如果发生错误，则返回 -1（在这种情况下，正确设置了 errno）。

ERRORS
       open(), openat(), and creat() can fail with the following errors:
       open()、openat() 和 creat() 可能会失败并出现以下错误:

       EACCES The requested access to the file is not allowed, or search permission is denied for one of the directories in the path prefix of pathname, or the file did  not  exist  yet
              and write access to the parent directory is not allowed.  (See also path_resolution(7).)
       EACCES 请求的文件访问不被允许，或者路径名的路径前缀中的目录之一的搜索权限被拒绝，或者文件还不存在并且不允许对父目录的写访问。 （另见 path_resolution(7)。）

       EDQUOT Where O_CREAT is specified, the file does not exist, and the user's quota of disk blocks or inodes on the filesystem has been exhausted.
       EDQUOT 指定 O_CREAT 的地方，文件不存在，用户在文件系统上的磁盘块或 inode 配额已用完。

       EEXIST pathname already exists and O_CREAT and O_EXCL were used.
       EEXIST 路径名已经存在，并且使用了 O_CREAT 和 O_EXCL。

       EFAULT pathname points outside your accessible address space.
       EFAULT 路径名指向您可访问的地址空间之外。

       EFBIG  See EOVERFLOW.
       EFBIG 请参见 EOVERFLOW。

       EINTR  While blocked waiting to complete an open of a slow device (e.g., a FIFO; see fifo(7)), the call was interrupted by a signal handler; see signal(7).
       EINTR 在阻塞等待完成打开慢速设备（例如，FIFO；参见 fifo(7)）时，调用被信号处理程序中断；见信号(7)。

       EINVAL The filesystem does not support the O_DIRECT flag.  See NOTES for more information.
       EINVAL 文件系统不支持 O_DIRECT 标志。有关详细信息，请参阅注释。

       EINVAL Invalid value in flags.
       EINVAL 标志中的无效值。

       EINVAL O_TMPFILE was specified in flags, but neither O_WRONLY nor O_RDWR was specified.
       EINVAL O_TMPFILE 在标志中指定，但 O_WRONLY 和 O_RDWR 均未指定。

       EINVAL O_CREAT  was  specified  in  flags and the final component ("basename") of the new file's pathname is invalid (e.g., it contains characters not permitted by the underlying
              filesystem).
       EINVAL O_CREAT 在标志中指定，并且新文件路径名的最终组成部分（“basename”）无效（例如，它包含底层文件系统不允许的字符）。

       EISDIR pathname refers to a directory and the access requested involved writing (that is, O_WRONLY or O_RDWR is set).
       EISDIR 路径名指的是一个目录，并且请求的访问涉及写入（即设置了 O_WRONLY 或 O_RDWR）。

       EISDIR pathname refers to an existing directory, O_TMPFILE and one of O_WRONLY or O_RDWR were specified in flags, but this kernel version does not provide the O_TMPFILE function‐
              ality.

       ELOOP  Too many symbolic links were encountered in resolving pathname.

       ELOOP  pathname was a symbolic link, and flags specified O_NOFOLLOW but not O_PATH.

       EMFILE The per-process limit on the number of open file descriptors has been reached (see the description of RLIMIT_NOFILE in getrlimit(2)).

       ENAMETOOLONG
              pathname was too long.

       ENFILE The system-wide limit on the total number of open files has been reached.

       ENODEV pathname refers to a device special file and no corresponding device exists.  (This is a Linux kernel bug; in this situation ENXIO must be returned.)

       ENOENT O_CREAT is not set and the named file does not exist.

       ENOENT A directory component in pathname does not exist or is a dangling symbolic link.

       ENOENT pathname  refers  to a nonexistent directory, O_TMPFILE and one of O_WRONLY or O_RDWR were specified in flags, but this kernel version does not provide the O_TMPFILE func‐
              tionality.

       ENOMEM The named file is a FIFO, but memory for the FIFO buffer can't be allocated because the per-user hard limit on memory allocation for pipes has been reached and the  caller
              is not privileged; see pipe(7).

       ENOMEM Insufficient kernel memory was available.

       ENOSPC pathname was to be created but the device containing pathname has no room for the new file.

       ENOTDIR
              A component used as a directory in pathname is not, in fact, a directory, or O_DIRECTORY was specified and pathname was not a directory.

       ENXIO  O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO open for reading.

       ENXIO  The file is a device special file and no corresponding device exists.

       ENXIO  The file is a UNIX domain socket.

       EOPNOTSUPP
              The filesystem containing pathname does not support O_TMPFILE.

       EOVERFLOW
              pathname  refers  to  a  regular  file  that  is too large to be opened.  The usual scenario here is that an application compiled on a 32-bit platform without -D_FILE_OFF‐
              SET_BITS=64 tried to open a file whose size exceeds (1<<31)-1 bytes; see also O_LARGEFILE above.  This is the error specified by POSIX.1; in kernels before  2.6.24,  Linux
              gave the error EFBIG for this case.

       EPERM  The O_NOATIME flag was specified, but the effective user ID of the caller did not match the owner of the file and the caller was not privileged.

       EPERM  The operation was prevented by a file seal; see fcntl(2).

       EROFS  pathname refers to a file on a read-only filesystem and write access was requested.

       ETXTBSY
              pathname refers to an executable image which is currently being executed and write access was requested.

       ETXTBSY
              pathname refers to a file that is currently in use as a swap file, and the O_TRUNC flag was specified.

       ETXTBSY
              pathname refers to a file that is currently being read by the kernel (e.g. for module/firmware loading), and write access was requested.

       EWOULDBLOCK
              The O_NONBLOCK flag was specified, and an incompatible lease was held on the file (see fcntl(2)).

       The following additional errors can occur for openat():

       EBADF  dirfd is not a valid file descriptor.

       ENOTDIR
              pathname is a relative pathname and dirfd is a file descriptor referring to a file other than a directory.

VERSIONS
       openat() was added to Linux in kernel 2.6.16; library support was added to glibc in version 2.4.
       openat() 在内核 2.6.16 中被添加到 Linux；库支持在 2.4 版中添加到 glibc。

CONFORMING TO
       open(), creat() SVr4, 4.3BSD, POSIX.1-2001, POSIX.1-2008.

       openat(): POSIX.1-2008.

       The O_DIRECT, O_NOATIME, O_PATH, and O_TMPFILE flags are Linux-specific.  One must define _GNU_SOURCE to obtain their definitions.
       O_DIRECT、O_NOATIME、O_PATH 和 O_TMPFILE 标志是 Linux 特定的。必须定义 _GNU_SOURCE 才能获得它们的定义。

       The  O_CLOEXEC,  O_DIRECTORY,  and  O_NOFOLLOW  flags are not specified in POSIX.1-2001, but are specified in POSIX.1-2008.  Since glibc 2.12, one can obtain their definitions by
       defining either _POSIX_C_SOURCE with a value greater than or equal to 200809L or _XOPEN_SOURCE with a value greater than or equal to 700.  In glibc 2.11 and earlier, one  obtains
       the definitions by defining _GNU_SOURCE.
       O_CLOEXEC、O_DIRECTORY 和 O_NOFOLLOW 标志未在 POSIX.1-2001 中指定，但在 POSIX.1-2008 中指定。从 glibc 2.12 开始，可以通过定义 _POSIX_C_SOURCE 的值大于或等于 200809L 或 _XOPEN_SOURCE 的值大于或等于 700 来获得它们的定义。在 glibc 2.11 及更早版本中，可以通过定义 _GNU_SOURCE 来获得定义。

       As noted in feature_test_macros(7), feature test macros such as _POSIX_C_SOURCE, _XOPEN_SOURCE, and _GNU_SOURCE must be defined before including any header files.
       如 feature_test_macros(7) 中所述，必须在包含任何头文件之前定义功能测试宏，例如 _POSIX_C_SOURCE、_XOPEN_SOURCE 和 _GNU_SOURCE。

NOTES
       Under  Linux,  the O_NONBLOCK flag is sometimes used in cases where one wants to open but does not necessarily have the intention to read or write.  For example, this may be used
       to open a device in order to get a file descriptor for use with ioctl(2).
       在 Linux 下，O_NONBLOCK 标志有时用于想要打开但不一定有读或写意图的情况。例如，这可用于打开设备以获取用于 ioctl(2) 的文件描述符。

       The (undefined) effect of O_RDONLY | O_TRUNC varies among implementations.  On many systems the file is actually truncated.
       O_RDONLY 的（未定义）效果 | O_TRUNC 因实现而异。在许多系统上，文件实际上被截断了。

       Note that open() can open device special files, but creat() cannot create them; use mknod(2) instead.
       注意 open() 可以打开设备专用文件，但是 creat() 不能创建它们；改用 mknod(2)。

       If the file is newly created, its st_atime, st_ctime, st_mtime fields (respectively, time of last access, time of last status change, and time of last modification; see  stat(2))
       are  set  to  the current time, and so are the st_ctime and st_mtime fields of the parent directory.  Otherwise, if the file is modified because of the O_TRUNC flag, its st_ctime
       and st_mtime fields are set to the current time.
       如果文件是新创建的，则其 st_atime、st_ctime、st_mtime 字段（分别为上次访问时间、上次状态更改时间和上次修改时间；参见 stat(2)）设置为当前时间，因此父目录的 st_ctime 和 st_mtime 字段。否则，如果文件因为 O_TRUNC 标志而被修改，则其 st_ctime 和 st_mtime 字段将设置为当前时间。

       The files in the /proc/[pid]/fd directory show the open file descriptors of the process with the PID pid.  The files in the /proc/[pid]/fdinfo directory show even  more  informa‐
       tion about these file descriptors.  See proc(5) for further details of both of these directories.
       /proc/[pid]/fd目录中的文件显示了pid为pid的进程打开的文件描述符。/proc/[pid]/fdinfo目录中的文件显示了关于这些文件描述符的更多信息。有关这两个目录的详细信息，请参阅proc(5)。

       The Linux header file <asm/fcntl.h> doesn't define O_ASYNC; the (BSD-derived) FASYNC synonym is defined instead.
       Linux头文件<asm/fcntl.h>没有定义O_ASYNC;取而代之的是定义了(bsd派生的)FASYNC同义词。

   Open file descriptions
   打开的文件描述符
       The  term  open file description is the one used by POSIX to refer to the entries in the system-wide table of open files.  In other contexts, this object is variously also called
       an "open file object", a "file handle", an "open file table entry", or—in kernel-developer parlance—a struct file.
       术语打开文件描述符是POSIX用来指打开文件的系统范围表中的条目。
       在其他环境中，这个对象也被称为“打开文件对象”、“文件句柄”、“打开文件表项”，或者用内核开发人员的话说就是一个结构文件。

       When a file descriptor is duplicated (using dup(2) or similar), the duplicate refers to the same open file description as the original file descriptor, and the two file  descrip‐
       tors consequently share the file offset and file status flags.  Such sharing can also occur between processes: a child process created via fork(2) inherits duplicates of its par‐
       ent's file descriptors, and those duplicates refer to the same open file descriptions.
       当一个文件描述符被复制时(使用dup(2)或类似的方法)，这个复制指向与原始文件描述符相同的打开的文件描述，这两个文件描述符因此共享文件偏移量和文件状态标志。
       这种共享也可以发生在进程之间:通过fork(2)创建的子进程继承其父进程的文件描述符的副本，这些副本引用相同的打开文件描述。

       Each open() of a file creates a new open file description; thus, there may be multiple open file descriptions corresponding to a file inode.
       每个文件的open()创建一个新的打开文件描述;因此，可能有多个打开的文件描述对应于一个文件inode。

       On Linux, one can use the kcmp(2) KCMP_FILE operation to test whether two file descriptors (in the same process or in two different processes) refer to the  same  open  file  de‐
       scription.
       在Linux上，可以使用kcmp(2) KCMP_FILE操作来测试两个文件描述符(在同一个进程中或在两个不同的进程中)是否引用同一个打开的文件描述。

   Synchronized I/O
   同步IO
       The  POSIX.1-2008  "synchronized I/O" option specifies different variants of synchronized I/O, and specifies the open() flags O_SYNC, O_DSYNC, and O_RSYNC for controlling the be‐
       havior.  Regardless of whether an implementation supports this option, it must at least support the use of O_SYNC for regular files.
       POSIX.1-2008“synchronized I/O”选项指定synchronized I/O的不同变体，并指定打开()标志O_SYNC、O_DSYNC和O_RSYNC来控制行为。无论实现是否支持此选项，它都必须至少支持对普通文件使用O_SYNC。

       Linux implements O_SYNC and O_DSYNC, but not O_RSYNC.  Somewhat incorrectly, glibc defines O_RSYNC to have the same value as O_SYNC.  (O_RSYNC is defined in the Linux header file
       <asm/fcntl.h> on HP PA-RISC, but it is not used.)
       Linux实现了O_SYNC和O_DSYNC，但不实现O_RSYNC。有些不正确的是，glibc将O_RSYNC定义为与O_SYNC相同的值。(O_RSYNC在Linux头文件中定义
        <asm/fcntl.h>在HP PA-RISC，但它没有被使用。)

       O_SYNC  provides  synchronized  I/O  file integrity completion, meaning write operations will flush data and all associated metadata to the underlying hardware.  O_DSYNC provides
       synchronized I/O data integrity completion, meaning write operations will flush data to the underlying hardware, but will only flush metadata updates that are required to allow a
       subsequent  read  operation  to  complete successfully.  Data integrity completion can reduce the number of disk operations that are required for applications that don't need the
       guarantees of file integrity completion.
       O_SYNC提供了同步的I/O文件完整性完成，这意味着写操作将把数据和所有相关的元数据刷新到底层硬件。
       O_DSYNC提供同步I/O数据完整性完成，这意味着写操作将刷新数据到底层硬件，但只会刷新元数据更新需要允许后续读操作成功完成。
       数据完整性完成可以减少不需要的应用程序所需的磁盘操作的数量保证文件完整完成。

       To understand the difference between the two types of completion, consider two pieces of file metadata: the file last modification timestamp (st_mtime) and the file length.   All
       write  operations will update the last file modification timestamp, but only writes that add data to the end of the file will change the file length.  The last modification time‐
       stamp is not needed to ensure that a read completes successfully, but the file length is.  Thus, O_DSYNC would only guarantee  to  flush  updates  to  the  file  length  metadata
       (whereas O_SYNC would also always flush the last modification timestamp metadata).
       要理解两种类型的完成之间的区别，请考虑两段文件元数据: 文件最后一次修改时间戳(st_mtime)和文件长度。
       所有写操作都会更新最后的文件修改时间戳，但只有在文件末尾添加数据的写操作才会改变文件长度。
       为了确保读操作成功完成，不需要最后一次修改时间戳，但是文件长度是。因此，O_DSYNC只保证刷新对文件长度元数据的更新(而O_SYNC还总是刷新最后修改时间戳元数据)。


       Before  Linux 2.6.33, Linux implemented only the O_SYNC flag for open().  However, when that flag was specified, most filesystems actually provided the equivalent of synchronized
       I/O data integrity completion (i.e., O_SYNC was actually implemented as the equivalent of O_DSYNC).

       Since Linux 2.6.33, proper O_SYNC support is provided.  However, to ensure backward binary compatibility, O_DSYNC was defined with the same value as the  historical  O_SYNC,  and
       O_SYNC was defined as a new (two-bit) flag value that includes the O_DSYNC flag value.  This ensures that applications compiled against new headers get at least O_DSYNC semantics
       on pre-2.6.33 kernels.

   C library/kernel differences
       Since version 2.26, the glibc wrapper function for open() employs the openat() system call, rather than the kernel's open() system call.  For certain architectures, this is  also
       true in glibc versions before 2.26.

   NFS
       There are many infelicities in the protocol underlying NFS, affecting amongst others O_SYNC and O_NDELAY.

       On  NFS  filesystems with UID mapping enabled, open() may return a file descriptor but, for example, read(2) requests are denied with EACCES.  This is because the client performs
       open() by checking the permissions, but UID mapping is performed by the server upon read and write requests.

   FIFOs
       Opening the read or write end of a FIFO blocks until the other end is also opened (by another process or thread).  See fifo(7) for further details.

   File access mode
       Unlike the other values that can be specified in flags, the access mode values O_RDONLY, O_WRONLY, and O_RDWR do not specify individual bits.  Rather, they define the  low  order
       two  bits  of  flags,  and  are defined respectively as 0, 1, and 2.  In other words, the combination O_RDONLY | O_WRONLY is a logical error, and certainly does not have the same
       meaning as O_RDWR.

       Linux reserves the special, nonstandard access mode 3 (binary 11) in flags to mean: check for read and write permission on the file and return a file  descriptor  that  can't  be
       used  for  reading or writing.  This nonstandard access mode is used by some Linux drivers to return a file descriptor that is to be used only for device-specific ioctl(2) opera‐
       tions.

   Rationale for openat() and other directory file descriptor APIs
       openat() and the other system calls and library functions that take a directory file descriptor argument (i.e., execveat(2), faccessat(2), fanotify_mark(2),  fchmodat(2),  fchow‐
       nat(2),  fstatat(2),  futimesat(2),  linkat(2), mkdirat(2), mknodat(2), name_to_handle_at(2), readlinkat(2), renameat(2), statx(2), symlinkat(2), unlinkat(2), utimensat(2), mkfi‐
       foat(3), and scandirat(3)) address two problems with the older interfaces that preceded them.  Here, the explanation is in terms of the openat() call, but the rationale is analo‐
       gous for the other interfaces.

       First,  openat()  allows  an application to avoid race conditions that could occur when using open() to open files in directories other than the current working directory.  These
       race conditions result from the fact that some component of the directory prefix given to open() could be changed in parallel with the call to open().  Suppose, for example, that
       we  wish  to  create the file dir1/dir2/xxx.dep if the file dir1/dir2/xxx exists.  The problem is that between the existence check and the file-creation step, dir1 or dir2 (which
       might be symbolic links) could be modified to point to a different location.  Such races can be avoided by opening a file descriptor for the target directory, and then specifying
       that file descriptor as the dirfd argument of (say) fstatat(2) and openat().  The use of the dirfd file descriptor also has other benefits:

       *  the file descriptor is a stable reference to the directory, even if the directory is renamed; and

       *  the open file descriptor prevents the underlying filesystem from being dismounted, just as when a process has a current working directory on a filesystem.

       Second, openat() allows the implementation of a per-thread "current working directory", via file descriptor(s) maintained by the application.  (This functionality can also be ob‐
       tained by tricks based on the use of /proc/self/fd/dirfd, but less efficiently.)

   O_DIRECT
       The O_DIRECT flag may impose alignment restrictions on the length and address of user-space buffers and the file offset of I/Os.  In Linux alignment restrictions vary by filesys‐
       tem and kernel version and might be absent entirely.  However there is currently no filesystem-independent interface for an application to discover these restrictions for a given
       file or filesystem.  Some filesystems provide their own interfaces for doing so, for example the XFS_IOC_DIOINFO operation in xfsctl(3).

       Under Linux 2.4, transfer sizes, and the alignment of the user buffer and the file offset must all be multiples of the logical block size of the filesystem.  Since  Linux  2.6.0,
       alignment  to the logical block size of the underlying storage (typically 512 bytes) suffices.  The logical block size can be determined using the ioctl(2) BLKSSZGET operation or
       from the shell using the command:

           blockdev --getss

       O_DIRECT I/Os should never be run concurrently with the fork(2) system call, if the memory buffer is a private mapping (i.e., any mapping created  with  the  mmap(2)  MAP_PRIVATE
       flag;  this includes memory allocated on the heap and statically allocated buffers).  Any such I/Os, whether submitted via an asynchronous I/O interface or from another thread in
       the process, should be completed before fork(2) is called.  Failure to do so can result in data corruption and undefined behavior in parent and child processes.  This restriction
       does  not apply when the memory buffer for the O_DIRECT I/Os was created using shmat(2) or mmap(2) with the MAP_SHARED flag.  Nor does this restriction apply when the memory buf‐
       fer has been advised as MADV_DONTFORK with madvise(2), ensuring that it will not be available to the child after fork(2).

       The O_DIRECT flag was introduced in SGI IRIX, where it has alignment restrictions similar to those of Linux 2.4.  IRIX has also a fcntl(2) call to query  appropriate  alignments,
       and sizes.  FreeBSD 4.x introduced a flag of the same name, but without alignment restrictions.

       O_DIRECT  support was added under Linux in kernel version 2.4.10.  Older Linux kernels simply ignore this flag.  Some filesystems may not implement the flag, in which case open()
       fails with the error EINVAL if it is used.

       Applications should avoid mixing O_DIRECT and normal I/O to the same file, and especially to overlapping byte regions in the same file.  Even when the filesystem  correctly  han‐
       dles  the  coherency  issues in this situation, overall I/O throughput is likely to be slower than using either mode alone.  Likewise, applications should avoid mixing mmap(2) of
       files with direct I/O to the same files.

       The behavior of O_DIRECT with NFS will differ from local filesystems.  Older kernels, or kernels configured in certain ways, may not support this combination.  The  NFS  protocol
       does not support passing the flag to the server, so O_DIRECT I/O will bypass the page cache only on the client; the server may still cache the I/O.  The client asks the server to
       make the I/O synchronous to preserve the synchronous semantics of O_DIRECT.  Some servers will perform poorly under these circumstances, especially if  the  I/O  size  is  small.
       Some  servers  may also be configured to lie to clients about the I/O having reached stable storage; this will avoid the performance penalty at some risk to data integrity in the
       event of server power failure.  The Linux NFS client places no alignment restrictions on O_DIRECT I/O.

       In summary, O_DIRECT is a potentially powerful tool that should be used with caution.  It is recommended that applications treat use of O_DIRECT as a performance option which  is
       disabled by default.

BUGS
       Currently, it is not possible to enable signal-driven I/O by specifying O_ASYNC when calling open(); use fcntl(2) to enable this flag.

       One must check for two different error codes, EISDIR and ENOENT, when trying to determine whether the kernel supports O_TMPFILE functionality.

       When both O_CREAT and O_DIRECTORY are specified in flags and the file specified by pathname does not exist, open() will create a regular file (i.e., O_DIRECTORY is ignored).

SEE ALSO
       chmod(2),  chown(2), close(2), dup(2), fcntl(2), link(2), lseek(2), mknod(2), mmap(2), mount(2), open_by_handle_at(2), read(2), socket(2), stat(2), umask(2), unlink(2), write(2),
       fopen(3), acl(5), fifo(7), inode(7), path_resolution(7), symlink(7)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of  this  page,  can  be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2020-02-09                                                                                OPEN(2)

]]

#[[

fopen();
open();

    r   ->  O_RDONLY
    r+  ->  O_RDWR
    w   ->  O_WRONLY|O_CREATE|O_TRUNC
    w+  ->  O_RDWR|O_TRUNC|O_CREATE (有则清空, 无则创建)
    a   ->  O_WRONLY|O_CREATE|O_APPEND
    a+  ->  O_RDWR|O_CREATE|O_APPEND

    ┌─────────────┬───────────────────────────────┐
    │fopen() mode │ open() flags                  │
    ├─────────────┼───────────────────────────────┤
    │     r       │ O_RDONLY                      │
    ├─────────────┼───────────────────────────────┤
    │     w       │ O_WRONLY | O_CREAT | O_TRUNC  │
    ├─────────────┼───────────────────────────────┤
    │     a       │ O_WRONLY | O_CREAT | O_APPEND │
    ├─────────────┼───────────────────────────────┤
    │     r+      │ O_RDWR                        │
    ├─────────────┼───────────────────────────────┤
    │     w+      │ O_RDWR | O_CREAT | O_TRUNC    │
    ├─────────────┼───────────────────────────────┤
    │     a+      │ O_RDWR | O_CREAT | O_APPEND   │
    └─────────────┴───────────────────────────────┘


fopen();创建一个文件的时候, 的权限是按照一个公式实现的 (0666 & ~umask)。

而open();创建一个文件的时候, 可以看到 open();函数有两个函数原型,
一个是2参数的, 一个是3参数的。
如果第二个参数 flags 当中有 O_CREATE, 那么一定要用3参的open();。
如果 flags 中没有 O_CREATE, 那么就用2参的open();。

因为C语言没有函数重载, 所以, open();函数是用变参函数实现的。

如何判断函数名相同的两个函数是用函数重载来实现的还是使用变参函数实现的?
可以这样, 调用这个函数, 然后给它传参数, 传4个5个6个等多个参数,
然后编译, 如果报的是语法错误, 那就是说明函数是定参, 定参那就说明使用重载实现的。
如果一编译, 没有报错, 语法上能过去, 这说明是变参实现的。因为这个函数本身也不知道自己有多少个参数。

3参的open();函数, mode, 指的是创建新文件的权限, 当然不会是你给什么, 它就是什么。
它会用 (mode & ~umask), 依然跟 (0666 & ~umask) 这个公式类似。

]]

#[[

CLOSE(2)                                                          Linux Programmer's Manual                                                          CLOSE(2)

NAME
       close - close a file descriptor

SYNOPSIS
       #include <unistd.h>

       int close(int fd);

DESCRIPTION
       close()  closes a file descriptor, so that it no longer refers to any file and may be reused.  Any record locks (see fcntl(2)) held on the file it was
       associated with, and owned by the process, are removed (regardless of the file descriptor that was used to obtain the lock).

       If fd is the last file descriptor referring to the underlying open file description (see open(2)), the resources associated with  the  open  file  de‐
       scription are freed; if the file descriptor was the last reference to a file which has been removed using unlink(2), the file is deleted.

RETURN VALUE
       close() returns zero on success.  On error, -1 is returned, and errno is set appropriately.
       close()成功返回0。当出现错误时，返回-1，并适当设置errno。

       close();函数的返回值很少去校验, 一般认为 close();函数不会出错。

ERRORS
       EBADF  fd isn't a valid open file descriptor.

       EINTR  The close() call was interrupted by a signal; see signal(7).

       EIO    An I/O error occurred.

       ENOSPC, EDQUOT
              On  NFS, these errors are not normally reported against the first write which exceeds the available storage space, but instead against a subse‐
              quent write(2), fsync(2), or close().

       See NOTES for a discussion of why close() should not be retried after an error.

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD.

NOTES
       A successful close does not guarantee that the data has been successfully saved to disk, as the kernel uses the buffer cache to defer  writes.   Typi‐
       cally,  filesystems do not flush buffers when a file is closed.  If you need to be sure that the data is physically stored on the underlying disk, use
       fsync(2).  (It will depend on the disk hardware at this point.)

       The close-on-exec file descriptor flag can be used to ensure that a file descriptor is automatically closed upon a successful execve(2); see  fcntl(2)
       for details.

       It  is probably unwise to close file descriptors while they may be in use by system calls in other threads in the same process.  Since a file descrip‐
       tor may be reused, there are some obscure race conditions that may cause unintended side effects.

   Dealing with error returns from close()
       A careful programmer will check the return value of close(), since it is quite possible that errors on a previous write(2) operation are reported only
       on  the final close() that releases the open file description.  Failing to check the return value when closing a file may lead to silent loss of data.
       This can especially be observed with NFS and with disk quota.

       Note, however, that a failure return should be used only for diagnostic purposes (i.e., a warning to the application that there may still be I/O pend‐
       ing or there may have been failed I/O) or remedial purposes (e.g., writing the file once more or creating a backup).

       Retrying  the close() after a failure return is the wrong thing to do, since this may cause a reused file descriptor from another thread to be closed.
       This can occur because the Linux kernel always releases the file descriptor early in the close operation, freeing it for reuse; the steps that may re‐
       turn an error, such as flushing data to the filesystem or device, occur only later in the close operation.

       Many other implementations similarly always close the file descriptor (except in the case of EBADF, meaning that the file descriptor was invalid) even
       if they subsequently report an error on return from close().  POSIX.1 is currently silent on this point, but there are plans to mandate this  behavior
       in the next major release of the standard.

       A careful programmer who wants to know about I/O errors may precede close() with a call to fsync(2).

       The EINTR error is a somewhat special case.  Regarding the EINTR error, POSIX.1-2013 says:

              If close() is interrupted by a signal that is to be caught, it shall return -1 with errno set to EINTR and the state of fildes is unspecified.

       This  permits  the behavior that occurs on Linux and many other implementations, where, as with other errors that may be reported by close(), the file
       descriptor is guaranteed to be closed.  However, it also permits another possibility: that the implementation returns an EINTR  error  and  keeps  the
       file  descriptor open.  (According to its documentation, HP-UX's close() does this.)  The caller must then once more use close() to close the file de‐
       scriptor, to avoid file descriptor leaks.  This divergence in implementation behaviors provides a difficult hurdle for portable applications, since on
       many  implementations,  close()  must not be called again after an EINTR error, and on at least one, close() must be called again.  There are plans to
       address this conundrum for the next major release of the POSIX.1 standard.

SEE ALSO
       fcntl(2), fsync(2), open(2), shutdown(2), unlink(2), fclose(3)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest ver‐
       sion of this page, can be found at https://www.kernel.org/doc/man-pages/.

Linux                                                                     2019-10-10                                                                 CLOSE(2)

]]


#[[

READ(2)                                                                         Linux Programmer's Manual                                                                         READ(2)

NAME
       read - read from a file descriptor

SYNOPSIS
       #include <unistd.h>

       // 读一个文件描述符fd(可以是一个成功打开的文件或者一个设备等),
       // 读取内容到 buf 中去, 读 count 个字节。

       ssize_t read(int fd, void *buf, size_t count);

DESCRIPTION
       read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.
       read()尝试从文件描述符fd读取 count 个字节数到从buf开始的缓冲区。

       On  files that support seeking, the read operation commences at the file offset, and the file offset is incremented by the number of bytes read.  If the file offset is at or past
       the end of file, no bytes are read, and read() returns zero.
       在支持查找的文件上，读取操作从文件偏移量开始，文件偏移量按读取的字节数递增。如果文件偏移量在文件末尾或超过文件末尾，则不读取字节，read()返回0。

       If count is zero, read() may detect the errors described below.  In the absence of any errors, or if read() does not check for errors, a read() with a count of 0 returns zero and
       has no other effects.
       如果count为零，read()可能检测到下面描述的错误。在没有任何错误的情况下，或者如果read()没有检查错误，那么计数为0的read()将返回零，并且没有其他效果。

       According to POSIX.1, if count is greater than SSIZE_MAX, the result is implementation-defined; see NOTES for the upper limit on Linux.
       根据POSIX.1，如果count大于SSIZE_MAX，结果是实现定义的;有关Linux上的上限，请参阅NOTES。


RETURN VALUE
       On  success,  the  number  of bytes read is returned (zero indicates end of file), and the file position is advanced by this number.  It is not an error if this number is smaller
       than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of-file,  or  because  we
       are reading from a pipe, or from a terminal), or because read() was interrupted by a signal.  See also NOTES.
       如果成功，则返回读取的字节数(0表示EOF)，并将文件位置提前该数字。
       如果这个数字小于请求的字节数，则不是错误;
       例如，这可能是因为现在实际可用的字节更少(可能是因为我们接近EOF，或者因为我们正在从管道或终端读取)，或者因为read()被一个信号中断。
       参见NOTES。

       如果调用成功的话, 返回的是成功读取到的字节数, 如果读到EOF, 那就返回0。

       On error, -1 is returned, and errno is set appropriately.  In this case, it is left unspecified whether the file position (if any) changes.
       当出现错误时，返回-1，并适当设置errno。在这种情况下，文件位置(如果有的话)是否改变将不指定。

ERRORS
       EAGAIN The file descriptor fd refers to a file other than a socket and has been marked nonblocking (O_NONBLOCK), and the read would block.  See open(2) for further details on the
              O_NONBLOCK flag.
       EAGAIN 文件描述符fd引用除了套接字以外的文件，并且被标记为非阻塞(O_NONBLOCK)，读取将阻塞。关于O_NONBLOCK标志的详细信息，请参阅open(2)。

       EAGAIN or EWOULDBLOCK
              The file descriptor fd refers to a socket and has been marked nonblocking (O_NONBLOCK), and the read would block.  POSIX.1-2001 allows either error to be returned for this
              case, and does not require these constants to have the same value, so a portable application should check for both possibilities.
       EAGAIN or EWOULDBLOCK
              文件描述符fd引用一个套接字，并且标记为非阻塞(O_NONBLOCK)，读取将阻塞。
              对于这种情况，POSIX.1-2001允许返回任何一个错误，并且不要求这些常量具有相同的值，因此可移植应用程序应该检查这两种可能性。

       EBADF  fd is not a valid file descriptor or is not open for reading.
       EBADF fd不是一个有效的文件描述符，或者没有打开以供读取。

       EFAULT buf is outside your accessible address space.
       EFAULT buf在您可访问的地址空间之外。

       EINTR  The call was interrupted by a signal before any data was read; see signal(7).
       EINTR 在读取任何数据之前，调用被一个信号中断;看到信号(7)。

       EINVAL fd is attached to an object which is unsuitable for reading; or the file was opened with the O_DIRECT flag, and either the address specified in buf, the value specified in
              count, or the file offset is not suitably aligned.

       EINVAL fd was created via a call to timerfd_create(2) and the wrong size buffer was given to read(); see timerfd_create(2) for further information.
       EINVAL Fd是通过调用timerfd_create(2)创建的，给read()的缓冲区大小是错误的;更多信息请参见timerfd_create(2)。

       EIO    I/O error.  This will happen for example when the process is in a background process group, tries to read from its controlling terminal,  and  either  it  is  ignoring  or
              blocking  SIGTTIN  or its process group is orphaned.  It may also occur when there is a low-level I/O error while reading from a disk or tape.  A further possible cause of
              EIO on networked filesystems is when an advisory lock had been taken out on the file descriptor and this lock has been lost.  See the Lost locks section  of  fcntl(2)  for
              further details.

       EISDIR fd refers to a directory.

       Other errors may occur, depending on the object connected to fd.
       根据连接到fd的对象的不同，可能会发生其他错误。

CONFORMING TO
       SVr4, 4.3BSD, POSIX.1-2001.

NOTES
       The types size_t and ssize_t are, respectively, unsigned and signed integer data types specified by POSIX.1.
       size_t和ssize_t类型分别是POSIX.1指定的无符号和有符号整数数据类型。

       On  Linux,  read()  (and  similar system calls) will transfer at most 0x7ffff000 (2,147,479,552) bytes, returning the number of bytes actually transferred.  (This is true on both
       32-bit and 64-bit systems.)
       在Linux上，read()(和类似的系统调用)最多传输0x7ffff000(2,147,479,552)字节，返回实际传输的字节数。(在32位和64位系统上都是如此。)

       On NFS filesystems, reading small amounts of data will update the timestamp only the first time, subsequent calls may not do so.  This is caused by client side attribute caching,
       because  most if not all NFS clients leave st_atime (last file access time) updates to the server, and client side reads satisfied from the client's cache will not cause st_atime
       updates on the server as there are no server-side reads.  UNIX semantics can be obtained by disabling client-side attribute caching, but in most  situations  this  will  substan‐
       tially increase server load and decrease performance.

BUGS
       According to POSIX.1-2008/SUSv4 Section XSI 2.9.7 ("Thread Interactions with Regular File Operations"):
       根据POSIX.1-2008/SUSv4 Section XSI 2.9.7(“线程与常规文件操作的交互”):

           All of the following functions shall be atomic with respect to each other in the effects specified in POSIX.1-2008 when they operate on regular files or symbolic links: ...
           以下所有函数在POSIX.1-2008中指定的效果中，当它们操作普通文件或符号链接时，应该是相互原子的:...

       Among  the APIs subsequently listed are read() and readv(2).  And among the effects that should be atomic across threads (and processes) are updates of the file offset.  However,
       on Linux before version 3.14, this was not the case: if two processes that share an open file description (see open(2)) perform a read() (or readv(2)) at the same time, then  the
       I/O  operations  were not atomic with respect updating the file offset, with the result that the reads in the two processes might (incorrectly) overlap in the blocks of data that
       they obtained.  This problem was fixed in Linux 3.14.
       随后列出的api包括read()和readv(2)。在跨线程(和进程)的影响中，应该是原子的是文件偏移量的更新。然而,
       在3.14版本之前的Linux上，情况并非如此: 如果两个进程共享一个打开的文件描述(见open(2))执行一个读()(或readv(2)同时,那么原子对I / O操作没有更新文件偏移量,
       结果读取两个进程可能(错误地)重叠块的数据获得。这个问题在Linux 3.14中已经修复。

SEE ALSO
       close(2), fcntl(2), ioctl(2), lseek(2), open(2), pread(2), readdir(2), readlink(2), readv(2), select(2), write(2), fread(3)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of  this  page,  can  be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2018-02-02                                                                                READ(2)

]]

#[[

WRITE(2)                                                                        Linux Programmer's Manual                                                                        WRITE(2)

NAME
       write - write to a file descriptor

SYNOPSIS
       #include <unistd.h>


       // 往 fd 内写内容, 内容来源 buf, 写 count 个字节。


       ssize_t write(int fd, const void *buf, size_t count);

DESCRIPTION
       write() writes up to count bytes from the buffer starting at buf to the file referred to by the file descriptor fd.
       write()从缓冲区从buf开始写到文件描述符fd所引用的文件，以 count 个字节。

       The  number  of bytes written may be less than count if, for example, there is insufficient space on the underlying physical medium, or the RLIMIT_FSIZE resource limit is encoun‐
       tered (see setrlimit(2)), or the call was interrupted by a signal handler after having written less than count bytes.  (See also pipe(7).)
       写入的字节数可能小于count，例如，如果底层物理介质上没有足够的空间，或者遇到了RLIMIT_FSIZE资源限制(参见setrlimit(2))，或者调用在写入小于count字节后被信号处理程序中断。(见也管(7)。)

       For a seekable file (i.e., one to which lseek(2) may be applied, for example, a regular file) writing takes place at the file offset, and the file offset is  incremented  by  the
       number of bytes actually written.  If the file was open(2)ed with O_APPEND, the file offset is first set to the end of the file before writing.  The adjustment of the file offset
       and the write operation are performed as an atomic step.
       对于一个可查找的文件(例如，一个可以应用lseek(2)的文件，例如，一个普通文件)，写入发生在文件偏移量，并且文件偏移量按实际写入的字节数递增。
       如果文件是用O_APPEND打开的，那么在写入之前，文件偏移量首先设置为文件的末尾。文件偏移量的调整和写入操作作为原子步骤执行。

       POSIX requires that a read(2) that can be proved to occur after a write() has returned will return the new data.  Note that not all filesystems are POSIX conforming.
       POSIX要求能够证明在write()返回后发生的read(2)将返回新数据。注意，不是所有的文件系统都符合POSIX。

       According to POSIX.1, if count is greater than SSIZE_MAX, the result is implementation-defined; see NOTES for the upper limit on Linux.
       根据POSIX.1，如果count大于SSIZE_MAX，结果是实现定义的;有关Linux上的上限，请参阅NOTES。

RETURN VALUE
       On success, the number of bytes written is returned.  On error, -1 is returned, and errno is set to indicate the cause of the error.
       如果成功，则返回写入的字节数。在出现错误时，返回-1，并设置errno以指示错误的原因。


       Note that a successful write() may transfer fewer than count bytes.  Such partial writes can occur for various reasons; for example, because there was insufficient space  on  the
       disk device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or similar was interrupted by a signal handler after it had transferred some, but
       before it had transferred all of the requested bytes.  In the event of a partial write, the caller can make another write() call to transfer the remaining bytes.  The  subsequent
       call will either transfer further bytes or may result in an error (e.g., if the disk is now full).
       注意，成功的write()可能传输的字节数少于count。
       这种局部写可能由于各种原因发生;
       例如，因为磁盘设备上没有足够的空间来写入所有请求的字节，或者因为阻塞的write()到套接字、管道或类似的操作在传输了一些字节之后被信号处理程序中断，但在它传输了所有请求的字节之前。
       在部分写入的情况下，调用者可以进行另一个write()调用来传输剩余的字节。随后的调用将进一步传输字节或可能导致错误(例如，如果磁盘现在已满)。

       所以, 才有了坚持写够多少个字节这样的设计, man手册也是推荐的。

       If  count is zero and fd refers to a regular file, then write() may return a failure status if one of the errors below is detected.  If no errors are detected, or error detection
       is not performed, 0 will be returned without causing any other effect.  If count is zero and fd refers to a file other than a regular file, the results are not specified.
       如果count为0并且fd指向一个常规文件，那么如果检测到以下错误之一，write()可能返回一个失败状态。
       如果没有检测到错误，或者没有执行错误检测，将返回0，而不会产生任何其他影响。如果count为0且fd指向的是普通文件以外的文件，则不指定结果。

ERRORS
这些 ERRORS 中有真错有假错, 如果发生了假错, 可能是被阻塞或者非阻塞一定会遇到的情况或者信号打断了。

       EAGAIN The file descriptor fd refers to a file other than a socket and has been marked nonblocking (O_NONBLOCK), and the write would block.  See open(2) for  further  details  on
              the O_NONBLOCK flag.
       EAGAIN 文件描述符fd引用套接字以外的文件，并且标记为非阻塞(O_NONBLOCK)，因此写入将会阻塞。关于O_NONBLOCK标志的详细信息，请参阅open(2)。

       EAGAIN or EWOULDBLOCK
              The  file  descriptor  fd  refers to a socket and has been marked nonblocking (O_NONBLOCK), and the write would block.  POSIX.1-2001 allows either error to be returned for
              this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.
       EAGAIN or EWOULDBLOCK
       文件描述符fd引用了一个套接字，并且标记为非阻塞(O_NONBLOCK)，因此写入将会阻塞。
       对于这种情况，POSIX.1-2001允许返回任何一个错误，并且不要求这些常量具有相同的值，因此可移植应用程序应该检查这两种可能性。

       EBADF  fd is not a valid file descriptor or is not open for writing.

       EDESTADDRREQ
              fd refers to a datagram socket for which a peer address has not been set using connect(2).

       EDQUOT The user's quota of disk blocks on the filesystem containing the file referred to by fd has been exhausted.

       EFAULT buf is outside your accessible address space.

       EFBIG  An attempt was made to write a file that exceeds the implementation-defined maximum file size or the process's file size limit, or to write at a position past the  maximum
              allowed offset.

       EINTR  The call was interrupted by a signal before any data was written; see signal(7).
       EINTR 在写入任何数据之前，调用被一个信号中断;看到信号(7)。

       EINVAL fd is attached to an object which is unsuitable for writing; or the file was opened with the O_DIRECT flag, and either the address specified in buf, the value specified in
              count, or the file offset is not suitably aligned.

       EIO    A low-level I/O error occurred while modifying the inode.  This error may relate to the write-back of data written by an earlier write(), which may have been issued  to  a
              different  file  descriptor on the same file.  Since Linux 4.13, errors from write-back come with a promise that they may be reported by subsequent.  write() requests, and
              will be reported by a subsequent fsync(2) (whether or not they were also reported by write()).  An alternate cause of EIO on networked filesystems is when an advisory lock
              had been taken out on the file descriptor and this lock has been lost.  See the Lost locks section of fcntl(2) for further details.

       ENOSPC The device containing the file referred to by fd has no room for the data.

       EPERM  The operation was prevented by a file seal; see fcntl(2).

       EPIPE  fd  is connected to a pipe or socket whose reading end is closed.  When this happens the writing process will also receive a SIGPIPE signal.  (Thus, the write return value
              is seen only if the program catches, blocks or ignores this signal.)
       EPIPE fd连接到一个读端关闭的管道或socket上。当这种情况发生时，写入进程也将收到一个SIGPIPE信号。(因此，只有当程序捕获、阻塞或忽略该信号时，才会看到write返回值。)

       Other errors may occur, depending on the object connected to fd.

CONFORMING TO
       SVr4, 4.3BSD, POSIX.1-2001.

       Under SVr4 a write may be interrupted and return EINTR at any point, not just before any data is written.

NOTES
       The types size_t and ssize_t are, respectively, unsigned and signed integer data types specified by POSIX.1.
       size_t和ssize_t类型分别是POSIX.1指定的无符号和有符号整数数据类型。

       A successful return from write() does not make any guarantee that data has been committed to disk.  On some filesystems, including NFS, it does not even guarantee that space  has
       successfully  been  reserved  for  the  data.   In this case, some errors might be delayed until a future write(), fsync(2), or even close(2).  The only way to be sure is to call
       fsync(2) after you are done writing all your data.
       write()的成功返回并不保证数据已经提交到磁盘。在一些文件系统(包括NFS)上，它甚至不保证已经成功地为数据保留了空间。
       在这种情况下，一些错误可能会延迟到将来的write()、fsync(2)甚至close(2)。唯一确定的方法是在写完所有数据后调用fsync(2)。

       If a write() is interrupted by a signal handler before any bytes are written, then the call fails with the error EINTR; if it is interrupted after at  least  one  byte  has  been
       written, the call succeeds, and returns the number of bytes written.
       如果write()在写入任何字节之前被信号处理程序中断，则调用失败，错误为EINTR;如果在至少一个字节写入之后中断，则调用成功，并返回写入的字节数。

       On  Linux,  write()  (and similar system calls) will transfer at most 0x7ffff000 (2,147,479,552) bytes, returning the number of bytes actually transferred.  (This is true on both
       32-bit and 64-bit systems.)
       在Linux上，write()(和类似的系统调用)最多传输0x7ffff000(2,147,479,552)字节，返回实际传输的字节数。(在32位和64位系统上都是如此。)

       An error return value while performing write() using direct I/O does not mean the entire write has failed. Partial data may be written and the data at the file  offset  on  which
       the write() was attempted should be considered inconsistent.
       使用直接I/O执行write()时的错误返回值并不意味着整个写操作失败。可能会写入部分数据，而在试图执行write()的文件偏移量处的数据应该被认为是不一致的。

BUGS
       According to POSIX.1-2008/SUSv4 Section XSI 2.9.7 ("Thread Interactions with Regular File Operations"):

           All of the following functions shall be atomic with respect to each other in the effects specified in POSIX.1-2008 when they operate on regular files or symbolic links: ...

       Among  the  APIs  subsequently listed are write() and writev(2).  And among the effects that should be atomic across threads (and processes) are updates of the file offset.  How‐
       ever, on Linux before version 3.14, this was not the case: if two processes that share an open file description (see open(2)) perform a write() (or writev(2)) at the  same  time,
       then  the  I/O  operations were not atomic with respect updating the file offset, with the result that the blocks of data output by the two processes might (incorrectly) overlap.
       This problem was fixed in Linux 3.14.

SEE ALSO
       close(2), fcntl(2), fsync(2), ioctl(2), lseek(2), open(2), pwrite(2), read(2), select(2), writev(2), fwrite(3)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of  this  page,  can  be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2019-10-10                                                                               WRITE(2)

]]

#[[

LSEEK(2)                                                          Linux Programmer's Manual                                                          LSEEK(2)

NAME
       lseek - reposition read/write file offset
       重新定位读写文件偏移量。

SYNOPSIS
       #include <sys/types.h>
       #include <unistd.h>

       // lseek();和fseek();非常的相似。
       // int fseek(FILE *stream, long offset, int whence);
       //
       // 对 fd 进行定位, 从 whence 这个位置偏移 offset 个字节。

       off_t lseek(int fd, off_t offset, int whence);

DESCRIPTION
       lseekfd to the argument offset according to the direc‐escription associated with the file descriptor
       tive whence as follows:

       // fseek();使用的也是这一套。SEEK_SET(文件开始)、SEEK_CUR(当前位置)、SEEK_END(末尾位置)。
       SEEK_SET
              The file offset is set to offset bytes.

       SEEK_CUR
              The file offset is set to its current location plus offset bytes.

       SEEK_END
              The file offset is set to the size of the file plus offset bytes.

       lseek() allows the file offset to be set beyond the end of the file (but this does not change the size of the file).  If data is later written at this
       point, subsequent reads of the data in the gap (a "hole") return null bytes ('\0') until data is actually written into the gap.
       lseek()允许将文件偏移量设置在文件末尾之外(但这不会改变文件的大小)。如果稍后在此时写入数据，那么对间隙(一个“洞”)中的数据的后续读取将返回null字节('\0')，直到数据真正写入间隙。

   Seeking file data and holes
   寻找文件数据和漏洞
       Since version 3.1, Linux supports the following additional values for whence:
       从3.1版开始，Linux支持下面的额外值:

       SEEK_DATA
              Adjust  the  file  offset to the next location in the file greater than or equal to offset containing data.  If offset points to data, then the
              file offset is set to offset.
              将文件的偏移量调整到文件中大于或等于包含数据的偏移量的下一个位置。如果offset指向数据，则文件offset被设置为offset。

       SEEK_HOLE
              Adjust the file offset to the next hole in the file greater than or equal to offset.  If offset points into the middle of a hole, then the file
              offset  is set to offset.  If there is no hole past offset, then the file offset is adjusted to the end of the file (i.e., there is an implicit
              hole at the end of any file).
              将文件偏移量调整到文件中大于或等于下一个孔的偏移量。如果偏移量指向孔的中间，则文件偏移量设置为偏移量。如果没有孔过去的偏移量，那么文件偏移量被调整到文件的末尾(即，在任何文件的末尾都有一个隐式的孔)。

       In both of the above cases, lseek() fails if offset points past the end of the file.
       在上述两种情况下，如果偏移点超过了文件的结尾，则lseek()将失败。

       These operations allow applications to map holes in a sparsely allocated file.  This can be useful for applications such as file backup  tools,  which
       can save space when creating backups and preserve holes, if they have a mechanism for discovering holes.

       For the purposes of these operations, a hole is a sequence of zeros that (normally) has not been allocated in the underlying file storage.  However, a
       filesystem is not obliged to report holes, so these operations are not a guaranteed mechanism for mapping the storage space actually  allocated  to  a
       file.  (Furthermore, a sequence of zeros that actually has been written to the underlying storage may not be reported as a hole.)  In the simplest im‐
       plementation, a filesystem can support the operations by making SEEK_HOLE always return the offset of the end of the file, and making SEEK_DATA always
       return offset (i.e., even if the location referred to by offset is a hole, it can be considered to consist of data that is a sequence of zeros).

       The _GNU_SOURCE feature test macro must be defined in order to obtain the definitions of SEEK_DATA and SEEK_HOLE from <unistd.h>.

       The SEEK_HOLE and SEEK_DATA operations are supported for the following filesystems:

       *  Btrfs (since Linux 3.1)

       *  OCFS (since Linux 3.2)

       *  XFS (since Linux 3.5)

       *  ext4 (since Linux 3.8)

       *  tmpfs(5) (since Linux 3.8)

       *  NFS (since Linux 3.18)

       *  FUSE (since Linux 4.5)

RETURN VALUE
       Upon  successful  completion,  lseek() returns the resulting offset location as measured in bytes from the beginning of the file.  On error, the value
       (off_t) -1 is returned and errno is set to indicate the error.
       成功完成后，lseek()返回结果偏移位置(以字节为单位)。在发生错误时，返回值(off_t) -1，并设置errno来指示错误。

       如果成功调用, lseek();返回从文件开始处到当前位置的字节数。
       lseek();其实相当于把 fseek();和ftell();进行了一个综合。

       如果调用失败, 返回 -1 , 并且设置 errno。


ERRORS
       EBADF  fd is not an open file descriptor.

       EINVAL whence is not valid.  Or: the resulting file offset would be negative, or beyond the end of a seekable device.

       ENXIO  whence is SEEK_DATA or SEEK_HOLE, and the file offset is beyond the end of the file.

       EOVERFLOW
              The resulting file offset cannot be represented in an off_t.

       ESPIPE fd is associated with a pipe, socket, or FIFO.

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD.

       SEEK_DATA and SEEK_HOLE are nonstandard extensions also present in Solaris, FreeBSD, and DragonFly BSD; they are proposed for inclusion  in  the  next
       POSIX revision (Issue 8).

NOTES
       See open(2) for a discussion of the relationship between file descriptors, open file descriptions, and files.

       If  the O_APPEND file status flag is set on the open file description, then a write(2) always moves the file offset to the end of the file, regardless
       of the use of lseek().

       The off_t data type is a signed integer data type specified by POSIX.1.

       Some devices are incapable of seeking and POSIX does not specify which devices must support lseek().

       On Linux, using lseek() on a terminal device fails with the error ESPIPE.

SEE ALSO
       dup(2), fallocate(2), fork(2), open(2), fseek(3), lseek64(3), posix_fallocate(3)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest ver‐
       sion of this page, can be found at https://www.kernel.org/doc/man-pages/.

Linux                                                                     2019-03-06                                                                 LSEEK(2)

]]

add_executable(sysio-MyCopy1 sysio-MyCopy1.cpp)
add_executable(sysio-MyCopy2 sysio-MyCopy2.cpp)
add_executable(sysio-MyCopy3 sysio-MyCopy3.cpp)

#[[

文件IO与标准IO的区别:

举例: 传达室老大爷跑邮局。
第一种方式: 拿到一封信就跑一趟邮局。一来一去 20 分钟过去了。
第二种方式: 老大爷最多拿到20封信后开始跑一趟邮局。(当缓冲区满了, 刷新一遍。)
          第二种方式会有一种情况是: 比如: 老大爷这已经拿到5封信了, 来第6个人的时候,
          这个人说我这封信非常着急, 要马上去寄。那么老大爷就马上拿着这6封信跑一趟邮局。
          (当调用fflush();时强制刷新缓冲区。)

标准IO就有缓冲这样的机制, 而系统调用IO时, 你每次调用都实打实的从user态切换到kernel态
去执行一次。系统调用IO的实时性非常的高！！！！

标准IO, 你看着我已经写入文件去了, 其实没有, 我是放到了缓冲区当中去了。
什么时候写到文件中去呢?
1. 缓冲区满了刷新缓冲区
2. 换行的时候刷新缓冲区
3. 调用fflush();强制刷新。


区别: 响应速度, 吞吐量。
系统调用IO的响应速度快,
标准IO的吞吐量大。

当然, 系统调用IO也有缓冲区, 属于内核缓冲区。

mst: 如何使一个程序变快?
这样的题要一分为二进行作答。因地制宜。具体情况具体分析。

是让响应速度变快?还是让程序的吞吐量变大?

让响应速度变快, 那就多用系统调用IO,
让吞吐量变大, 那就多用标准IO。

从用户的角度去体验觉得程序变快了, 其实不是响应速度, 而是吞吐量。
所以, 尽量优先选择使用标准IO, 当然, 系统调用IO合适的话, 那就
先使用系统调用IO。
最主要的是先把程序顺手的写出来, 然后再进行不断的重构！！！！
这才是要关注的重点！！！！


但是要注意: 标准IO和系统调用IO一定一定一定不要混合使用！！！！

即使是同一个文件也不要用着用着标准IO, 然后使用系统调用IO。因为 FILE 中的 pos 和
与文件关联的结构体中的 pos 基本上一定是不一样的。

使用 fputc();等函数写文件的时候, 并没有写到文件实处, 即不是内容就直接写到磁盘上文件中了,
可能的情况是这样的: fputc();往缓冲区(buffer)中写, 然后刷新缓冲区后, 与文件关联的
结构体中的 pos 一下就 写了这整个缓冲区的内容。
或者使用 fgetc();等函数读文件的时候, 可能是这样的与文件关联的结构体中的 pos 一下
读了 1024 个字节到缓冲区(cache), 然后 fgetc();函数再一个一个从缓冲区中读字节。

即与文件关联的结构体中的 pos 往往与 FILE 中的 pos 是不同的,
所以一定一定一定不要混用标准IO和系统调用IO。




fileno();

FERROR(3)                                                         Linux Programmer's Manual                                                         FERROR(3)

NAME
       clearerr, feof, ferror, fileno - check and reset stream status
       检查和重置流状态。

SYNOPSIS
       #include <stdio.h>

       void clearerr(FILE *stream);

       int feof(FILE *stream);

       int ferror(FILE *stream);


       // 这个函数会把标准IO中使用的 FILE* 转换为 系统调用IO/文件IO 中使用的 fd。
       // 还有一个函数是 fdopen();。

       int fileno(FILE *stream);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       fileno(): _POSIX_C_SOURCE

DESCRIPTION
       The function clearerr() clears the end-of-file and error indicators for the stream pointed to by stream.
       函数 clearerr() 清除流指向的流的EOF和错误指示符。

       The  function  feof()  tests the end-of-file indicator for the stream pointed to by stream, returning nonzero if it is set.  The end-of-file indicator
       can be cleared only by the function clearerr().
       函数 feof() 测试流指向的流的EOF，如果设置了则返回非零值。文件结束指示符只能由函数 clearerr() 清除。

       The function ferror() tests the error indicator for the stream pointed to by stream, returning nonzero if it is set.  The error indicator can be reset
       only by the clearerr() function.
       函数 ferror() 测试流指向的流的错误指示符，如果已设置则返回非零值。错误指示器只能通过 clearerr() 函数重置。

       The  function  fileno()  examines  the  argument stream and returns the integer file descriptor used to implement this stream.  The file descriptor is
       still owned by stream and will be closed when fclose(3) is called.  Duplicate the file descriptor with dup(2) before passing it  to  code  that  might
       close it.
       函数 fileno() 检查参数流并返回用于实现此流的整数文件描述符。文件描述符仍然归流所有，并且将在调用 fclose(3) 时关闭。在将文件描述符传递给可能关闭它的代码之前，使用 dup(2) 复制文件描述符。

       For nonlocking counterparts, see unlocked_stdio(3).
       对于非锁定对应项，请参阅 unlocked_stdio(3)。

ERRORS
       These  functions  should  not  fail  and  do not set the external variable errno.  (However, in case fileno() detects that its argument is not a valid
       stream, it must return -1 and set errno to EBADF.)
       这些函数不应失败，也不要设置外部变量 errno。 （但是，如果 fileno() 检测到它的参数不是一个有效的流，它必须返回 -1 并将 errno 设置为 EBADF。）

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌────────────────────┬───────────────┬─────────┐
       │Interface           │ Attribute     │ Value   │
       ├────────────────────┼───────────────┼─────────┤
       │clearerr(), feof(), │ Thread safety │ MT-Safe │
       │ferror(), fileno()  │               │         │
       └────────────────────┴───────────────┴─────────┘
CONFORMING TO
       The functions clearerr(), feof(), and ferror() conform to C89, C99, POSIX.1-2001, and POSIX.1-2008.

       The function fileno() conforms to POSIX.1-2001 and POSIX.1-2008.

SEE ALSO
       open(2), fdopen(3), stdio(3), unlocked_stdio(3)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest ver‐
       sion of this page, can be found at https://www.kernel.org/doc/man-pages/.

                                                                          2019-03-06                                                                FERROR(3)

]]

add_executable(sysio-ab1 sysio-ab1.cpp)

#[[

strace命令可以查看一个可执行文件的系统调用是如何发生的。跟gdb不一样。

liuyou@liuyou-virtual-machine:~/CLionProjects/LinuxEnvironmentProgramming2/src/io/sysio$ strace ./ab
execve("./ab", ["./ab"], 0x7ffdede0d1f0 /* 25 vars */) = 0
brk(NULL)                               = 0x5585e7620000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffc9e539bc0) = -1 EINVAL (无效的参数)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (没有那个文件或目录)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=61441, ...}) = 0
mmap(NULL, 61441, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f7192a0b000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libstdc++.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\341\t\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=1956992, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7192a09000
mmap(NULL, 1972224, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f7192827000
mprotect(0x7f71928bd000, 1290240, PROT_NONE) = 0
mmap(0x7f71928bd000, 987136, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x96000) = 0x7f71928bd000
mmap(0x7f71929ae000, 299008, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x187000) = 0x7f71929ae000
mmap(0x7f71929f8000, 57344, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1d0000) = 0x7f71929f8000
mmap(0x7f7192a06000, 10240, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f7192a06000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360q\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
fstat(3, {st_mode=S_IFREG|0755, st_size=2029224, ...}) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
mmap(NULL, 2036952, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f7192635000
mprotect(0x7f719265a000, 1847296, PROT_NONE) = 0
mmap(0x7f719265a000, 1540096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7f719265a000
mmap(0x7f71927d2000, 303104, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19d000) = 0x7f71927d2000
mmap(0x7f719281d000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f719281d000
mmap(0x7f7192823000, 13528, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f7192823000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\363\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=1369352, ...}) = 0
mmap(NULL, 1368336, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f71924e6000
mmap(0x7f71924f5000, 684032, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf000) = 0x7f71924f5000
mmap(0x7f719259c000, 618496, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xb6000) = 0x7f719259c000
mmap(0x7f7192633000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x14c000) = 0x7f7192633000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\3405\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=104984, ...}) = 0
mmap(NULL, 107592, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f71924cb000
mmap(0x7f71924ce000, 73728, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7f71924ce000
mmap(0x7f71924e0000, 16384, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15000) = 0x7f71924e0000
mmap(0x7f71924e4000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18000) = 0x7f71924e4000
close(3)                                = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f71924c9000
arch_prctl(ARCH_SET_FS, 0x7f71924c9f40) = 0
mprotect(0x7f719281d000, 12288, PROT_READ) = 0
mprotect(0x7f71924e4000, 4096, PROT_READ) = 0
mprotect(0x7f7192633000, 4096, PROT_READ) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f71924c7000
mprotect(0x7f71929f8000, 45056, PROT_READ) = 0
mprotect(0x5585e718f000, 4096, PROT_READ) = 0
mprotect(0x7f7192a48000, 4096, PROT_READ) = 0
munmap(0x7f7192a0b000, 61441)           = 0
brk(NULL)                               = 0x5585e7620000
brk(0x5585e7641000)                     = 0x5585e7641000
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x3), ...}) = 0
write(1, "b", 1b)                        = 1
write(1, "b", 1b)                        = 1
write(1, "b", 1b)                        = 1
# write();支撑起了所有的标准IO的写操作函数。
write(1, "aaa", 3aaa)                      = 3
exit_group(0)                           = ?
+++ exited with 0 +++
liuyou@liuyou-virtual-machine:~/CLionProjects/LinuxEnvironmentProgramming2/src/io/sysio$

]]

#[[

IO的效率问题

例如: 使用 read();和write();实现的 MyCopy程序中:
将 字符数组buf 所创建出来的缓冲区的大小是 1024,
那么将 BUF_SIZE 设置为 1 或者 10 或者 100 或者 1000 有没有区别?

BUF_SIZE: 1 2 4 8 16 24 ...

当BUF_SIZE两倍两倍增长的时候, 执行的效率(拷贝的效率) 应该是直线上升的。
那么肯定有一个拐点, 有一个性能最佳的拐点。
当BUF_SIZE增长到某个值的时候, 当前的拷贝效率不但不提高, 反而降低。

./mycopy3 srcfile destfile

time ./mycopy3 srcfile destfile
time命令指的是测试后面的程序执行的时间。

liuyou@liuyou-virtual-machine:~/CLionProjects/LinuxEnvironmentProgramming2/src/io/sysio$ time ./mycopy3 /etc/services /tmp/out

# real的值理论上是 user的值 + sys的值 + 再加一点点值。
# 为什么 real时间比user+sys时间要多一点点,
# 因为这个程序有调度等待的时间(时间片轮转, 或者被某个信号打断等等)。
# 用户真正感觉的到的是 real这个时间。
# 而对于程序员来讲, 我们在意的是 user+sys 的时间, 在user消耗的时间和在sys消耗的时间。
# 程序员力争减少这两部分的时间。
real    0m0.002s
# user这个时间指的是这个程序再user层面消耗的时间。
user    0m0.002s
# sys这个时间是当前这个程序在系统调用层面(或者说是kernel层面)执行消耗的时间。
sys     0m0.000s
liuyou@liuyou-virtual-machine:~/CLionProjects/LinuxEnvironmentProgramming2/src/io/sysio$ time ./mycopy3 /etc/services /tmp/out

real    0m0.004s
user    0m0.000s
sys     0m0.004s
liuyou@liuyou-virtual-machine:~/CLionProjects/LinuxEnvironmentProgramming2/src/io/sysio$ time ./mycopy3 /etc/services /tmp/out

real    0m0.003s
user    0m0.000s
sys     0m0.002s
liuyou@liuyou-virtual-machine:~/CLionProjects/LinuxEnvironmentProgramming2/src/io/sysio$



这个BUF_SIZE从 128B 个字节增长到 16MB。

如果不更改系统的设置, 当BUF_SIZE增长到 16MB 的期间某个大小时, 会出段错误。
因为栈的大小(栈空间的)默认是 8MB(8192kbytes)。

liuyou@liuyou-virtual-machine:~/CLionProjects/LinuxEnvironmentProgramming2$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 15374
max locked memory       (kbytes, -l) 65536
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 15374
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
liuyou@liuyou-virtual-machine:~/CLionProjects/LinuxEnvironmentProgramming2$

注意性能最佳拐点出现时的BUF_SIZE的值, 以及程序何时会出问题。

]]

#[[

文件共享

这个 int fds[1024]; 这个数组是存在每个进程的进程空间内的。
每个进程都有一个这样的表。即使两个进程打开了同一个文件,
那么产生的与文件(inode)关联的结构体也是放在这两个进程的进程空间内, 互不干扰。

mst: 写程序删除一个文件的第10行。
使用lseek();定位到11行的行首, 然后使用read();读,
然后lseek();定位到第10行的行首, 然后使用write();覆盖写。

另外一种思路:
在一个进程空间打开两次这个文件。
1. open O_RDONLY -> fd1
fd1 lseek(); 11行的位置,
2. open O_RDWR   -> fd2
fd2 lseek(); 10行的位置,

while() {
通过 fd1  然后read();
然后 fd2  然后write();
}

还有一个思路:
用两个进程。
进程1 open(); O_RDONLY
进程2 open(); O_RDWR

这个思路需要用到进程间通信。

进程1 read(); 然后把读的内容交给 进程2 然后 进程2 write();

还有一个思路:
用两个线程。


truncate();
TRUNCATE(2)                                                                     Linux Programmer's Manual                                                                     TRUNCATE(2)

NAME
       truncate, ftruncate - truncate a file to a specified length
       将文件截断到指定长度。

SYNOPSIS
       #include <unistd.h>
       #include <sys/types.h>


       // 把一个未打开的文件 path 截断到 length 长度。

       int truncate(const char *path, off_t length);


       // 把一个已经打开的文件 fd, 截断到 length 长度。

       int ftruncate(int fd, off_t length);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       truncate():
           _XOPEN_SOURCE >= 500
               || /* Since glibc 2.12: */ _POSIX_C_SOURCE >= 200809L
               || /* Glibc versions <= 2.19: */ _BSD_SOURCE

       ftruncate():
           _XOPEN_SOURCE >= 500
               || /* Since glibc 2.3.5: */ _POSIX_C_SOURCE >= 200112L
               || /* Glibc versions <= 2.19: */ _BSD_SOURCE

DESCRIPTION
       The truncate() and ftruncate() functions cause the regular file named by path or referenced by fd to be truncated to a size of precisely length bytes.

       If the file previously was larger than this size, the extra data is lost.  If the file previously was shorter, it is extended, and the extended part reads as null bytes ('\0').

       The file offset is not changed.

       If  the  size  changed, then the st_ctime and st_mtime fields (respectively, time of last status change and time of last modification; see inode(7)) for the file are updated, and
       the set-user-ID and set-group-ID mode bits may be cleared.

       With ftruncate(), the file must be open for writing; with truncate(), the file must be writable.
       使用 ftruncate()，文件必须打开才能写入；使用 truncate()，文件必须是可写的。

RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.
       成功时，返回零。出错时，返回 -1，并适当设置 errno。

ERRORS
       For truncate():

       EACCES Search permission is denied for a component of the path prefix, or the named file is not writable by the user.  (See also path_resolution(7).)

       EFAULT The argument path points outside the process's allocated address space.

       EFBIG  The argument length is larger than the maximum file size. (XSI)

       EINTR  While blocked waiting to complete, the call was interrupted by a signal handler; see fcntl(2) and signal(7).

       EINVAL The argument length is negative or larger than the maximum file size.

       EIO    An I/O error occurred updating the inode.

       EISDIR The named file is a directory.

       ELOOP  Too many symbolic links were encountered in translating the pathname.

       ENAMETOOLONG
              A component of a pathname exceeded 255 characters, or an entire pathname exceeded 1023 characters.

       ENOENT The named file does not exist.

       ENOTDIR
              A component of the path prefix is not a directory.

       EPERM  The underlying filesystem does not support extending a file beyond its current size.

       EPERM  The operation was prevented by a file seal; see fcntl(2).

       EROFS  The named file resides on a read-only filesystem.

       ETXTBSY
              The file is an executable file that is being executed.

       For ftruncate() the same errors apply, but instead of things that can be wrong with path, we now have things that can be wrong with the file descriptor, fd:

       EBADF  fd is not a valid file descriptor.

       EBADF or EINVAL
              fd is not open for writing.

       EINVAL fd does not reference a regular file or a POSIX shared memory object.

       EINVAL or EBADF
              The file descriptor fd is not open for writing.  POSIX permits, and portable applications should handle, either error for this case.  (Linux produces EINVAL.)

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008, 4.4BSD, SVr4 (these calls first appeared in 4.2BSD).

NOTES
       ftruncate() can also be used to set the size of a POSIX shared memory object; see shm_open(7).

       The details in DESCRIPTION are for XSI-compliant systems.  For non-XSI-compliant systems, the POSIX standard allows two behaviors for ftruncate() when  length  exceeds  the  file
       length (note that truncate() is not specified at all in such an environment): either returning an error, or extending the file.  Like most UNIX implementations, Linux follows the
       XSI requirement when dealing with native filesystems.  However, some nonnative filesystems do not permit truncate() and ftruncate() to be used to extend a file beyond its current
       length: a notable example on Linux is VFAT.

       The  original  Linux  truncate() and ftruncate() system calls were not designed to handle large file offsets.  Consequently, Linux 2.4 added truncate64() and ftruncate64() system
       calls that handle large files.  However, these details can be ignored by applications using glibc, whose wrapper functions transparently employ the more recent system calls where
       they are available.

       On some 32-bit architectures, the calling signature for these system calls differ, for the reasons described in syscall(2).

BUGS
       A  header  file bug in glibc 2.12 meant that the minimum value of _POSIX_C_SOURCE required to expose the declaration of ftruncate() was 200809L instead of 200112L.  This has been
       fixed in later glibc versions.

SEE ALSO
       truncate(1), open(2), stat(2), path_resolution(7)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of  this  page,  can  be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2019-03-06                                                                            TRUNCATE(2)

]]

#[[

原子操作

]]

#[[

程序中的重定向: dup dup2

]]
add_executable(sysio-dup1 sysio-dup1.cpp)
add_executable(sysio-dup2 sysio-dup2.cpp)
#[[

dup();
DUP(2)                                                            Linux Programmer's Manual                                                            DUP(2)

NAME
       dup, dup2, dup3 - duplicate a file descriptor
       复制文件描述符。

SYNOPSIS
       #include <unistd.h>


       int dup(int oldfd);

       //

       int dup2(int oldfd, int newfd);

       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <fcntl.h>              /* Obtain O_* constant definitions */
       #include <unistd.h>

       int dup3(int oldfd, int newfd, int flags);

DESCRIPTION
       The dup() system call creates a copy of the file descriptor oldfd, using the lowest-numbered unused file descriptor for the new descriptor.
       dup() 系统调用创建文件描述符 oldfd 的副本，使用编号最低的未使用文件描述符作为新描述符。

       dup();使用当前可用范围内最小的文件描述符, 作为新的文件描述符。

       After  a  successful return, the old and new file descriptors may be used interchangeably.  They refer to the same open file description (see open(2))
       and thus share file offset and file status flags; for example, if the file offset is modified by using lseek(2) on one of the  file  descriptors,  the
       offset is also changed for the other.
       成功返回后，新旧文件描述符可以互换使用。它们引用相同的打开文件描述（请参阅 open(2)），因此共享文件偏移量和文件状态标志；
       例如，如果在一个文件描述符上使用 lseek(2) 修改了文件偏移量，则另一个文件描述符的偏移量也会更改。

       The  two  file descriptors do not share file descriptor flags (the close-on-exec flag).  The close-on-exec flag (FD_CLOEXEC; see fcntl(2)) for the du‐
       plicate descriptor is off.
       这两个文件描述符不共享文件描述符标志（close-on-exec 标志）。重复描述符的 close-on-exec 标志（FD_CLOEXEC；参见 fcntl(2)）关闭。

   dup2()
       int dup2(int oldfd, int newfd);
       把 newfd 作为一个 oldfd 的一个副本, 如果 newfd 被占用的话,
       dup2();会把 newfd close(); 然后把 oldfd 的副本放到 newfd 上去。
       而且 dup2();保证关闭和重用 newfd 这个文件描述符的步骤是原子的。


       The dup2() system call performs the same task as dup(), but instead of using the lowest-numbered unused file descriptor, it uses the  file  descriptor
       number specified in newfd.  If the file descriptor newfd was previously open, it is silently closed before being reused.
       dup2() 系统调用执行与 dup() 相同的任务，但它不使用编号最低的未使用文件描述符，而是使用 newfd 中指定的文件描述符编号。
       如果文件描述符 newfd 先前已打开，则在重用之前它会被静默关闭。

       The  steps of closing and reusing the file descriptor newfd are performed atomically.  This is important, because trying to implement equivalent func‐
       tionality using close(2) and dup() would be subject to race conditions, whereby newfd might be reused between the two steps.  Such reuse could  happen
       because  the  main program is interrupted by a signal handler that allocates a file descriptor, or because a parallel thread allocates a file descrip‐
       tor.
       关闭和重用文件描述符 newfd 的步骤是原子执行的。
       这很重要，因为尝试使用 close(2) 和 dup() 实现等效功能会受到竞争条件的影响，从而可能会在两个步骤之间重用 newfd。
       这种重用可能发生，因为主程序被分配文件描述符的信号处理程序中断，或者因为并行线程分配文件描述符。

       Note the following points:
       请注意以下几点:

       *  If oldfd is not a valid file descriptor, then the call fails, and newfd is not closed.
       * 如果 oldfd 不是有效的文件描述符，则调用失败，并且 newfd 不会关闭。

       *  If oldfd is a valid file descriptor, and newfd has the same value as oldfd, then dup2() does nothing, and returns newfd.
       * 如果 oldfd 是有效的文件描述符，并且 newfd 具有与 oldfd 相同的值，则 dup2() 什么都不做，并返回 newfd。

       * 如果 oldfd 是一个有效的文件描述符, 而且 newfd 和 oldfd 是同样的值的话,
         那么 dup2(); 什么都不做, 并且返回 当前 oldfd 本身。

   dup3()
       dup3() is the same as dup2(), except that:
       dup3() 与 dup2() 相同，除了:

       *  The caller can force the close-on-exec flag to be set for the new file descriptor by specifying O_CLOEXEC in flags.  See  the  description  of  the
          same flag in open(2) for reasons why this may be useful.
       * 调用者可以通过在 flags 中指定 O_CLOEXEC 来强制为新文件描述符设置 close-on-exec 标志。请参阅 open(2) 中相同标志的描述，了解这可能有用的原因。

       *  If oldfd equals newfd, then dup3() fails with the error EINVAL.
       * 如果 oldfd 等于 newfd，则 dup3() 失败并显示错误 EINVAL。

RETURN VALUE
       On success, these system calls return the new file descriptor.  On error, -1 is returned, and errno is set appropriately.
       成功时，这些系统调用返回新的文件描述符。出错时，返回 -1，并适当设置 errno。

ERRORS
       EBADF  oldfd isn't an open file descriptor.

       EBADF  newfd is out of the allowed range for file descriptors (see the discussion of RLIMIT_NOFILE in getrlimit(2)).

       EBUSY  (Linux only) This may be returned by dup2() or dup3() during a race condition with open(2) and dup().

       EINTR  The dup2() or dup3() call was interrupted by a signal; see signal(7).

       EINVAL (dup3()) flags contain an invalid value.

       EINVAL (dup3()) oldfd was equal to newfd.

       EMFILE The per-process limit on the number of open file descriptors has been reached (see the discussion of RLIMIT_NOFILE in getrlimit(2)).

VERSIONS
       dup3() was added to Linux in version 2.6.27; glibc support is available starting with version 2.9.
       dup3() 在 2.6.27 版本中被添加到 Linux 中；从 2.9 版开始提供 glibc 支持。

CONFORMING TO
       dup(), dup2(): POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD.

       dup3() is Linux-specific.
       dup3()是linux特有的。

NOTES
       The  error  returned  by dup2() is different from that returned by fcntl(..., F_DUPFD, ...)  when newfd is out of range.  On some systems, dup2() also
       sometimes returns EINVAL like F_DUPFD.

       If newfd was open, any errors that would have been reported at close(2) time are lost.  If this is of concern,  then—unless  the  program  is  single-
       threaded  and  does  not allocate file descriptors in signal handlers—the correct approach is not to close newfd before calling dup2(), because of the
       race condition described above.  Instead, code something like the following could be used:

           /* Obtain a duplicate of 'newfd' that can subsequently
              be used to check for close() errors; an EBADF error
              means that 'newfd' was not open. */

           tmpfd = dup(newfd);
           if (tmpfd == -1 && errno != EBADF) {
               /* Handle unexpected dup() error */
           }

           /* Atomically duplicate 'oldfd' on 'newfd' */

           if (dup2(oldfd, newfd) == -1) {
               /* Handle dup2() error */
           }

           /* Now check for close() errors on the file originally
              referred to by 'newfd' */

           if (tmpfd != -1) {
               if (close(tmpfd) == -1) {
                   /* Handle errors from close */
               }
           }

SEE ALSO
       close(2), fcntl(2), open(2)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest ver‐
       sion of this page, can be found at https://www.kernel.org/doc/man-pages/.

Linux                                                                     2017-09-15                                                                   DUP(2)


]]
add_executable(sysio-dup3 sysio-dup3.cpp)
add_executable(sysio-dup4 sysio-dup4.cpp)
add_executable(sysio-dup5 sysio-dup5.cpp)

#[[

同步 Synchronize
sync: 同步
async: 异步

sync();
fsync();
fdatasync();

SYNC(2)                                                           Linux Programmer's Manual                                                           SYNC(2)

NAME
       sync, syncfs - commit filesystem caches to disk
       将文件系统缓存提交到磁盘。
       提交 文件系统缓存到 磁盘。

       提交的是内核层面的 buffer 和 cache 。

       在解除设备挂载的时候, 例如: 当内核即将解除设备挂载的时候,
       我会在当前 buffer 和 cache 当前没有同步的数据刷新写入到 磁盘上。
       然后下一步执行其它的等等操作, 然后解除设备挂载。
       所以进行设备编程的时候, 进行全局催促会用到sync();这个函数。

SYNOPSIS
       #include <unistd.h>

       void sync(void);

       int syncfs(int fd);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       sync():
           _XOPEN_SOURCE >= 500
               || /* Since glibc 2.19: */ _DEFAULT_SOURCE
               || /* Glibc versions <= 2.19: */ _BSD_SOURCE

       syncfs():
           _GNU_SOURCE

DESCRIPTION
       sync() causes all pending modifications to filesystem metadata and cached file data to be written to the underlying filesystems.
       sync() 会导致对文件系统元数据和缓存文件数据的所有未决修改写入底层文件系统。

       syncfs() is like sync(), but synchronizes just the filesystem containing file referred to by the open file descriptor fd.
       syncfs() 与 sync() 类似，但只同步包含由打开的文件描述符 fd 引用的文件的文件系统。

RETURN VALUE
       syncfs() returns 0 on success; on error, it returns -1 and sets errno to indicate the error.
       syncfs() 成功返回 0；出错时，它返回 -1 并设置 errno 以指示错误。

ERRORS
       sync() is always successful.
       sync() 总是成功的。

       syncfs() can fail for at least the following reason:
       syncfs() 至少可能由于以下原因而失败:

       EBADF  fd is not a valid file descriptor.
       EBADF fd 不是有效的文件描述符。

VERSIONS
       syncfs() first appeared in Linux 2.6.39; library support was added to glibc in version 2.14.
       syncfs() 首次出现于 Linux 2.6.39；库支持在 2.14 版中添加到 glibc。

CONFORMING TO
       sync(): POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD.

       syncfs() is Linux-specific.

NOTES
       Since  glibc  2.2.2,  the  Linux  prototype  for  sync() is as listed above, following the various standards.  In glibc 2.2.1 and earlier, it was "int
       sync(void)", and sync() always returned 0.
       从 glibc 2.2.2 开始，sync() 的 Linux 原型如上所列，遵循各种标准。在 glibc 2.2.1 及更早版本中，它是“int sync(void)”，而 sync() 总是返回 0。

       According to the standard specification (e.g., POSIX.1-2001), sync() schedules the writes, but may return before the actual writing is done.   However
       Linux waits for I/O completions, and thus sync() or syncfs() provide the same guarantees as fsync called on every file in the system or filesystem re‐
       spectively.
       根据标准规范（例如 POSIX.1-2001），sync() 计划写入，但可能会在实际写入完成之前返回。然而，Linux 等待 IO 完成，因此 sync() 或 syncfs() 提供与分别在系统或文件系统中的每个文件上调用 fsync 相同的保证。

BUGS
       Before version 1.3.20 Linux did not wait for I/O to complete before returning.

SEE ALSO
       sync(1), fdatasync(2), fsync(2)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest ver‐
       sion of this page, can be found at https://www.kernel.org/doc/man-pages/.

Linux                                                                     2017-09-15                                                                  SYNC(2)

FSYNC(2)                                                          Linux Programmer's Manual                                                          FSYNC(2)

NAME
       fsync, fdatasync - synchronize a file's in-core state with storage device
       将文件的核心状态与存储设备同步。

SYNOPSIS
       #include <unistd.h>

       // 同步一个 fd 的 buffer 和 cache等等文件的核心状态。

       int fsync(int fd);

       // 这个函数是只刷新数据, 不刷新亚数据(文件最后的修改时间, 文件的属性等等这样的内容)。

       int fdatasync(int fd);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       fsync():
           Glibc 2.16 and later:
               No feature test macros need be defined
           Glibc up to and including 2.15:
               _BSD_SOURCE || _XOPEN_SOURCE
                   || /* since glibc 2.8: */ _POSIX_C_SOURCE >= 200112L
       fdatasync():
           _POSIX_C_SOURCE >= 199309L || _XOPEN_SOURCE >= 500

DESCRIPTION
       fsync()  transfers  ("flushes") all modified in-core data of (i.e., modified buffer cache pages for) the file referred to by the file descriptor fd to
       the disk device (or other permanent storage device) so that all changed information can be retrieved even if the system crashes or is rebooted.   This
       includes writing through or flushing a disk cache if present.  The call blocks until the device reports that the transfer has completed.

       As well as flushing the file data, fsync() also flushes the metadata information associated with the file (see inode(7)).

       Calling  fsync()  does not necessarily ensure that the entry in the directory containing the file has also reached disk.  For that an explicit fsync()
       on a file descriptor for the directory is also needed.

       fdatasync() is similar to fsync(), but does not flush modified metadata unless that metadata is needed in order to allow a subsequent  data  retrieval
       to be correctly handled.  For example, changes to st_atime or st_mtime (respectively, time of last access and time of last modification; see inode(7))
       do not require flushing because they are not necessary for a subsequent data read to be handled correctly.  On the other hand, a change  to  the  file
       size (st_size, as made by say ftruncate(2)), would require a metadata flush.

       The aim of fdatasync() is to reduce disk activity for applications that do not require all metadata to be synchronized with the disk.

RETURN VALUE
       On success, these system calls return zero.  On error, -1 is returned, and errno is set appropriately.

ERRORS
       EBADF  fd is not a valid open file descriptor.

       EIO    An  error  occurred during synchronization.  This error may relate to data written to some other file descriptor on the same file.  Since Linux
              4.13, errors from write-back will be reported to all file descriptors that might have  written  the  data  which  triggered  the  error.   Some
              filesystems  (e.g., NFS) keep close track of which data came through which file descriptor, and give more precise reporting.  Other filesystems
              (e.g., most local filesystems) will report errors to all file descriptors that were open on the file when the error was recorded.

       ENOSPC Disk space was exhausted while synchronizing.

       EROFS, EINVAL
              fd is bound to a special file (e.g., a pipe, FIFO, or socket) which does not support synchronization.

       ENOSPC, EDQUOT
              fd is bound to a file on NFS or another filesystem which does not allocate space at the time of a write(2) system call, and some previous write
              failed due to insufficient storage space.

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008, 4.3BSD.

AVAILABILITY
       On POSIX systems on which fdatasync() is available, _POSIX_SYNCHRONIZED_IO is defined in <unistd.h> to a value greater than 0.  (See also sysconf(3).)


NOTES
       On some UNIX systems (but not Linux), fd must be a writable file descriptor.

       In Linux 2.2 and earlier, fdatasync() is equivalent to fsync(), and so has no performance advantage.

       The  fsync() implementations in older kernels and lesser used filesystems do not know how to flush disk caches.  In these cases disk caches need to be
       disabled using hdparm(8) or sdparm(8) to guarantee safe operation.

SEE ALSO
       sync(1), bdflush(2), open(2), posix_fadvise(2), pwritev(2), sync(2), sync_file_range(2), fflush(3), fileno(3), hdparm(8), mount(8)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest ver‐
       sion of this page, can be found at https://www.kernel.org/doc/man-pages/.

Linux                                                                     2019-03-06                                                                 FSYNC(2)


]]

#[[

file control
fcntl(); 文件描述符fd所变的魔术几乎都来源于这个函数。
管家级别的函数, 因为 fcntl();函数的功能很多, 比较杂。

鉴于 man手册 对 fcntl(); 的描述非常长, 所以没有贴过来, 只贴了一部分。

FCNTL(2)                                                                        Linux Programmer's Manual                                                                        FCNTL(2)

NAME
       fcntl - manipulate file descriptor
       操作文件描述符。

SYNOPSIS
       #include <unistd.h>
       #include <fcntl.h>

       // 对着个 fd 做 cmd(什么样的事情)
       // 你要做的这件事情是否需要传什么参数。
       //
       // 由于 cmd参数(命令) 不同, 会造成所传的参数不同。
       // 由于 cmd参数(命令)不同, 会造成 fcntl();函数的返回值不同。
       int fcntl(int fd, int cmd, ... /* arg */ );

       cmd有哪些内容呢?
       例如:
            复制一个文件描述符(和dup();功能相似。其实dup();就是使用fcntl();来封装设计出来的, dup2();也是由fcntl();封装出来的。)
            修改文件描述符相关的 flags (即 open();时候指定的第二个参数 flags。)
                修改文件状态 flags(也是open();时的第二个参数 flags 的一些可选项。)
            文件锁之类的

RETURN VALUE
       由于调用 fcntl();传入了不同的 cmd参数, 会导致 fcntl();的返回值是不同的。


       For a successful call, the return value depends on the operation:
       对于成功调用，返回值取决于操作:

       F_DUPFD  The new file descriptor.
       如果用的 cmd 是 F_DUPFD, 那么返回值就是一个新的文件描述符。

       F_GETFD  Value of file descriptor flags.
       如果传入的参数 cmd 是 F_GETFD, 那么返回值就是 文件描述符的 flags。

       F_GETFL  Value of file status flags.
       如果传入的参数 cmd 是 F_GETFL(这是跟文件的状态 flags相关的), 返回值也是一个文件的文件状态flags。

       F_GETLEASE
                Type of lease held on file descriptor.

       F_GETOWN Value of file descriptor owner.

       F_GETSIG Value of signal sent when read or write becomes possible, or zero for traditional SIGIO behavior.

       F_GETPIPE_SZ, F_SETPIPE_SZ
                The pipe capacity.

       F_GET_SEALS
                A bit mask identifying the seals that have been set for the inode referred to by fd.

       All other commands
                Zero.

       On error, -1 is returned, and errno is set appropriately.

]]

#[[

ioctl(); 和fcntl();一样, ioctl();是另外一个管家函数。

与设备相关的内容, 都归 ioctl();管。

一切皆文件的设计好不好?
一切皆文件在与设备开发相关的程序员并不友好。
从事与设备开发相关的工作会用到它。

]]

#[[

/dev/fd/目录: 虚目录, 显示的是当前进程的文件描述符信息。

]]

#[[
IO

    FILE

    fd


]]


#[[
删除一个文件的第10行。
]]
add_executable(sysio-del1 sysio-del1.cpp)
#[[
第二个版本。
]]
add_executable(sysio-del2 sysio-del2.cpp)
#[[
第三个版本。
这个版本设计出来, 可以将第10行删除, 但是没有加截断。在下个版本中进行完善。
]]
add_executable(sysio-del3 sysio-del3.cpp)
#[[
第四个版本, 加入了文件截断, 成功实现了功能。
]]
add_executable(sysio-del4 sysio-del4.cpp)
# 最终的版本。
add_executable(sysio-del5 sysio-del5.cpp)












































































































